Merge "DO NOT MERGE Implement USB High Temperature warning dialog(1/N)" into pi-dev
am: f147660c1e
Change-Id: Icdba0dc71c79a5d630be327d0ff3f9d0044ce60e
This commit is contained in:
39
packages/SystemUI/res/layout/overheat_dialog_content.xml
Normal file
39
packages/SystemUI/res/layout/overheat_dialog_content.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<!--
|
||||
Copyright (C) 2018 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/contentPanel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="48dp">
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/scrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="@dimen/alarm_dialog_panel_padding"
|
||||
android:paddingEnd="?android:attr/dialogPreferredPadding"
|
||||
android:paddingStart="?android:attr/dialogPreferredPadding"
|
||||
android:clipToPadding="false">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/high_temp_alarm_notify_message"
|
||||
style="@android:style/TextAppearance.Material.Subhead"/>
|
||||
</ScrollView>
|
||||
</FrameLayout>
|
||||
46
packages/SystemUI/res/layout/overheat_dialog_title.xml
Normal file
46
packages/SystemUI/res/layout/overheat_dialog_title.xml
Normal file
@@ -0,0 +1,46 @@
|
||||
<!--
|
||||
Copyright (C) 2018 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/title_template"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical|start"
|
||||
android:paddingStart="?android:attr/dialogPreferredPadding"
|
||||
android:paddingEnd="?android:attr/dialogPreferredPadding"
|
||||
android:paddingTop="@dimen/alarm_dialog_panel_padding">
|
||||
|
||||
<ImageView
|
||||
android:id="@android:id/icon"
|
||||
android:layout_width="32dip"
|
||||
android:layout_height="32dip"
|
||||
android:layout_marginEnd="8dip"
|
||||
android:scaleType="fitCenter"
|
||||
android:tint="?android:attr/colorError"
|
||||
android:src="?android:attr/alertDialogIcon"/>
|
||||
|
||||
<com.android.internal.widget.DialogTitle
|
||||
android:id="@+id/alertTitle"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="viewStart"
|
||||
android:text="@string/high_temp_alarm_title"
|
||||
android:importantForAccessibility="noHideDescendants"
|
||||
style="?android:attr/windowTitleStyle"/>
|
||||
</LinearLayout>
|
||||
BIN
packages/SystemUI/res/raw/overheat_alarm.ogg
Normal file
BIN
packages/SystemUI/res/raw/overheat_alarm.ogg
Normal file
Binary file not shown.
@@ -365,17 +365,31 @@
|
||||
|
||||
<bool name="quick_settings_show_full_alarm">false</bool>
|
||||
|
||||
<!-- Whether or not beep sound should be when overheat -->
|
||||
<bool name="config_alarmTemperatureBeepSound">false</bool>
|
||||
|
||||
<!-- Whether to show a warning notification when the device reaches a certain temperature. -->
|
||||
<integer name="config_showTemperatureWarning">0</integer>
|
||||
|
||||
<!-- Whether to show a alarm dialog when device of usb cable reaches a certain temperature. -->
|
||||
<integer name="config_showTemperatureAlarm">0</integer>
|
||||
|
||||
<!-- Temp at which to show a warning notification if config_showTemperatureWarning is true.
|
||||
If < 0, uses the skin temperature sensor shutdown value from
|
||||
HardwarePropertiesManager#getDeviceTemperatures - config_warningTemperatureTolerance. -->
|
||||
<integer name="config_warningTemperature">-1</integer>
|
||||
|
||||
<!-- Temp at which to show a alarm dialog if config_showTemperatureAlarm is true.
|
||||
If < 0, uses the skin temperature sensor shutdown value of index[1] from
|
||||
HardwarePropertiesManager#getDeviceTemperatures -->
|
||||
<integer name="config_alarmTemperature">60</integer>
|
||||
|
||||
<!-- Fudge factor for how much below the shutdown temp to show the warning. -->
|
||||
<integer name="config_warningTemperatureTolerance">2</integer>
|
||||
|
||||
<!-- Fudge factor for how much below the overheat temp to dismiss alarm. -->
|
||||
<integer name="config_alarmTemperatureTolerance">5</integer>
|
||||
|
||||
<!-- Accessibility actions -->
|
||||
<item type="id" name="action_split_task_to_left" />
|
||||
<item type="id" name="action_split_task_to_right" />
|
||||
|
||||
@@ -1023,4 +1023,11 @@
|
||||
<!-- How much we expand the touchable region of the status bar below the notch to catch touches
|
||||
that just start below the notch. -->
|
||||
<dimen name="display_cutout_touchable_region_size">12dp</dimen>
|
||||
|
||||
<!-- Padding for overheat alarm dialog message of content -->
|
||||
<dimen name="alarm_dialog_panel_padding">24dp</dimen>
|
||||
|
||||
<!-- Padding for overheat alarm dialog message of content -->
|
||||
<dimen name="alarm_dialog_message_space">28dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -2069,6 +2069,14 @@
|
||||
<string name="high_temp_notif_message">Some features limited while phone cools down</string>
|
||||
<!-- Text body for dialog alerting user that their phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=300] -->
|
||||
<string name="high_temp_dialog_message">Your phone will automatically try to cool down. You can still use your phone, but it may run slower.\n\nOnce your phone has cooled down, it will run normally.</string>
|
||||
<!-- Title for alarm dialog alerting user the usb adapter has reached a certain temperature that should disconnect charging cable immediately. [CHAR LIMIT=30] -->
|
||||
<string name="high_temp_alarm_title">Unplug charger</string>
|
||||
<!-- Text body for dialog alerting user the usb adapter has reached a certain temperature that should disconnect charging cable immediately. [CHAR LIMIT=300] -->
|
||||
<string name="high_temp_alarm_notify_message">There\u2019s an issue charging this device. Unplug the power adapter and take care as the cable may be warm.</string>
|
||||
<!-- Text link for user to see more detail about overheat alarm. [CHAR LIMIT=300] -->
|
||||
<string name="high_temp_alarm_help_care_steps">See care steps</string>
|
||||
<!-- Help link of context for usb overheat web page -->
|
||||
<string name="high_temp_alarm_help_url" translatable="false">help_uri_usb_warm</string>
|
||||
|
||||
<!-- SysUI Tuner: Button to select lock screen shortcut [CHAR LIMIT=60] -->
|
||||
<string name="lockscreen_shortcut_left">Left shortcut</string>
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.power;
|
||||
|
||||
import static android.content.Context.VIBRATOR_SERVICE;
|
||||
|
||||
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.media.AudioAttributes;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.Vibrator;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.media.NotificationPlayer;
|
||||
|
||||
/**
|
||||
* A Controller handle beep sound, vibration and TTS depend on state of OverheatAlarmDialog.
|
||||
*/
|
||||
public class OverheatAlarmController {
|
||||
private static final String TAG = OverheatAlarmController.class.getSimpleName();
|
||||
|
||||
private static final int VIBRATION_INTERVAL = 2000;
|
||||
private static final long[] VIBRATION_PATTERN = new long[]{0, 400, 200, 400, 200, 400, 200};
|
||||
|
||||
private static OverheatAlarmController sInstance;
|
||||
|
||||
private final Vibrator mVibrator;
|
||||
|
||||
private NotificationPlayer mPlayer;
|
||||
private VibrationEffect mVibrationEffect;
|
||||
|
||||
private boolean mShouldVibrate;
|
||||
|
||||
/**
|
||||
* The constructor only used to create singleton sInstance.
|
||||
*/
|
||||
private OverheatAlarmController(Context context) {
|
||||
mVibrator = (Vibrator) context.getSystemService(VIBRATOR_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get singleton OverheatAlarmController instance.
|
||||
*/
|
||||
public static OverheatAlarmController getInstance(Context context) {
|
||||
if (context == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (sInstance == null) {
|
||||
sInstance = new OverheatAlarmController(context);
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starting alarm beep sound and vibration.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void startAlarm(Context context) {
|
||||
if (mPlayer != null) {
|
||||
return;
|
||||
}
|
||||
playSound(context);
|
||||
startVibrate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop alarming beep sound, vibrating, and TTS if initialized.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void stopAlarm() {
|
||||
stopPlayer();
|
||||
stopVibrate();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected void playSound(Context context) {
|
||||
Uri uri = new Uri.Builder()
|
||||
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
|
||||
.authority(context.getBasePackageName())
|
||||
.appendPath(Integer.toString(R.raw.overheat_alarm)).build();
|
||||
|
||||
if (mPlayer == null) {
|
||||
mPlayer = new NotificationPlayer(TAG);
|
||||
}
|
||||
mPlayer.setUsesWakeLock(context);
|
||||
mPlayer.play(context, uri, true /* looping */, getAlertAudioAttributes());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected void stopPlayer() {
|
||||
if (mPlayer != null) {
|
||||
mPlayer.stop();
|
||||
mPlayer = null;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected void startVibrate() {
|
||||
mShouldVibrate = true;
|
||||
if (mVibrationEffect == null) {
|
||||
mVibrationEffect = VibrationEffect.createWaveform(VIBRATION_PATTERN, -1);
|
||||
}
|
||||
performVibrate();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected void performVibrate() {
|
||||
if (mShouldVibrate && mVibrator != null) {
|
||||
mVibrator.vibrate(mVibrationEffect, getAlertAudioAttributes());
|
||||
Handler.getMain().sendMessageDelayed(
|
||||
obtainMessage(OverheatAlarmController::performVibrate, this),
|
||||
VIBRATION_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected void stopVibrate() {
|
||||
if (mVibrator != null) {
|
||||
mVibrator.cancel();
|
||||
}
|
||||
mShouldVibrate = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build AudioAttributes for mPlayer(NotificationPlayer) and vibrator
|
||||
* Use the alarm channel so it can vibrate in DnD mode, unless alarms are
|
||||
* specifically disabled in DnD.
|
||||
*/
|
||||
private static AudioAttributes getAlertAudioAttributes() {
|
||||
AudioAttributes.Builder builder = new AudioAttributes.Builder();
|
||||
builder.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION);
|
||||
builder.setUsage(AudioAttributes.USAGE_ALARM);
|
||||
// Set FLAG_BYPASS_INTERRUPTION_POLICY and FLAG_BYPASS_MUTE so that it enables
|
||||
// audio in any DnD mode, even in total silence DnD mode (requires MODIFY_PHONE_STATE).
|
||||
builder.setFlags(
|
||||
AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY | AudioAttributes.FLAG_BYPASS_MUTE);
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.power;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* The alarm dialog shown when the device overheats.
|
||||
* When the temperature exceeds a threshold, we're showing this dialog to notify the user.
|
||||
* Once the dialog shows, a sound and vibration will be played until the user touches the dialog.
|
||||
*/
|
||||
public class OverheatAlarmDialog extends AlertDialog {
|
||||
private final OverheatDialogDelegate mOverheatDialogDelegate;
|
||||
private final View mContentView, mTitleView;
|
||||
|
||||
private static boolean sHasUserInteracted;
|
||||
|
||||
private OverheatAlarmDialog.PowerEventReceiver mPowerEventReceiver;
|
||||
|
||||
/**
|
||||
* OverheatAlarmDialog should appear over system panels and keyguard.
|
||||
*/
|
||||
public OverheatAlarmDialog(Context context) {
|
||||
super(context, R.style.Theme_SystemUI_Dialog_Alert);
|
||||
mOverheatDialogDelegate = new OverheatDialogDelegate();
|
||||
|
||||
// Setup custom views, the purpose of set custom title and message is inject
|
||||
// AccessibilityDelegate to solve beep sound and talk back mix problem
|
||||
final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
|
||||
Context.LAYOUT_INFLATER_SERVICE);
|
||||
mContentView = inflater.inflate(R.layout.overheat_dialog_content, null);
|
||||
mTitleView = inflater.inflate(R.layout.overheat_dialog_title, null);
|
||||
setView(mContentView);
|
||||
setCustomTitle(mTitleView);
|
||||
|
||||
setupDialog();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismiss() {
|
||||
sHasUserInteracted = false;
|
||||
getContext().unregisterReceiver(mPowerEventReceiver);
|
||||
super.dismiss();
|
||||
}
|
||||
|
||||
private void setupDialog() {
|
||||
getWindow().getAttributes().privateFlags |=
|
||||
WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
|
||||
getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
|
||||
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
|
||||
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
|
||||
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
|
||||
// Register ACTION_SCREEN_OFF for power Key event.
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||
mPowerEventReceiver = new OverheatAlarmDialog.PowerEventReceiver();
|
||||
getContext().registerReceiverAsUser(mPowerEventReceiver, UserHandle.CURRENT, filter, null,
|
||||
null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(CharSequence title) {
|
||||
if (mTitleView == null) {
|
||||
super.setTitle(title);
|
||||
return;
|
||||
}
|
||||
final TextView titleTextView = mTitleView.findViewById(R.id.alertTitle);
|
||||
if (titleTextView != null) {
|
||||
titleTextView.setText(title);
|
||||
titleTextView.setAccessibilityDelegate(mOverheatDialogDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessage(CharSequence message) {
|
||||
if (mContentView == null) {
|
||||
super.setMessage(message);
|
||||
return;
|
||||
}
|
||||
final TextView messageView = mContentView.findViewById(android.R.id.message);
|
||||
if (messageView != null) {
|
||||
messageView.setAccessibilityDelegate(mOverheatDialogDelegate);
|
||||
messageView.requestAccessibilityFocus();
|
||||
messageView.setText(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||
if (!sHasUserInteracted) {
|
||||
switch (event.getKeyCode()) {
|
||||
case KeyEvent.KEYCODE_VOLUME_UP:
|
||||
case KeyEvent.KEYCODE_VOLUME_DOWN:
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
notifyAlarmBeepSoundChange();
|
||||
sHasUserInteracted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return super.dispatchKeyEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
// Stop beep sound when touch alarm dialog.
|
||||
if (!sHasUserInteracted) {
|
||||
if (ev.getAction() == MotionEvent.ACTION_DOWN
|
||||
|| ev.getAction() == MotionEvent.ACTION_OUTSIDE) {
|
||||
notifyAlarmBeepSoundChange();
|
||||
sHasUserInteracted = true;
|
||||
}
|
||||
}
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected void notifyAlarmBeepSoundChange() {
|
||||
this.getContext().sendBroadcast(new Intent(Intent.ACTION_ALARM_CHANGED).setPackage(
|
||||
this.getContext().getPackageName())
|
||||
.setFlags(Intent.FLAG_RECEIVER_FOREGROUND));
|
||||
}
|
||||
|
||||
private final class PowerEventReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (!sHasUserInteracted) {
|
||||
notifyAlarmBeepSoundChange();
|
||||
sHasUserInteracted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement AccessibilityDelegate to stop beep sound while title or message view get
|
||||
* accessibility focus, in case the alarm beep sound mix up talk back description.
|
||||
*/
|
||||
private final class OverheatDialogDelegate extends View.AccessibilityDelegate {
|
||||
@Override
|
||||
public boolean performAccessibilityAction(View host, int action, Bundle args) {
|
||||
if (action == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS && !sHasUserInteracted) {
|
||||
notifyAlarmBeepSoundChange();
|
||||
sHasUserInteracted = true;
|
||||
}
|
||||
return super.performAccessibilityAction(host, action, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
package com.android.systemui.power;
|
||||
|
||||
import static android.content.DialogInterface.BUTTON_NEGATIVE;
|
||||
import static android.content.DialogInterface.BUTTON_POSITIVE;
|
||||
|
||||
import android.app.KeyguardManager;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
@@ -47,10 +51,13 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
|
||||
import com.android.settingslib.Utils;
|
||||
import com.android.settingslib.fuelgauge.BatterySaverUtils;
|
||||
import com.android.settingslib.utils.PowerUtil;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SystemUI;
|
||||
import com.android.systemui.plugins.ActivityStarter;
|
||||
import com.android.systemui.statusbar.phone.SystemUIDialog;
|
||||
import com.android.systemui.util.NotificationChannels;
|
||||
import com.android.systemui.volume.Events;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.text.NumberFormat;
|
||||
@@ -111,6 +118,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
|
||||
private final Context mContext;
|
||||
private final NotificationManager mNoMan;
|
||||
private final PowerManager mPowerMan;
|
||||
private final KeyguardManager mKeyguard;
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
private final Receiver mReceiver = new Receiver();
|
||||
private final Intent mOpenBatterySettings = settings(Intent.ACTION_POWER_USAGE_SUMMARY);
|
||||
@@ -134,25 +142,37 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
|
||||
private boolean mHighTempWarning;
|
||||
private SystemUIDialog mHighTempDialog;
|
||||
private SystemUIDialog mThermalShutdownDialog;
|
||||
@VisibleForTesting
|
||||
protected OverheatAlarmDialog mOverheatAlarmDialog;
|
||||
|
||||
public PowerNotificationWarnings(Context context) {
|
||||
mContext = context;
|
||||
mNoMan = mContext.getSystemService(NotificationManager.class);
|
||||
mPowerMan = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||
mKeyguard = mContext.getSystemService(KeyguardManager.class);
|
||||
mReceiver.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(PrintWriter pw) {
|
||||
pw.print("mWarning="); pw.println(mWarning);
|
||||
pw.print("mPlaySound="); pw.println(mPlaySound);
|
||||
pw.print("mInvalidCharger="); pw.println(mInvalidCharger);
|
||||
pw.print("mShowing="); pw.println(SHOWING_STRINGS[mShowing]);
|
||||
pw.print("mSaverConfirmation="); pw.println(mSaverConfirmation != null ? "not null" : null);
|
||||
pw.print("mWarning=");
|
||||
pw.println(mWarning);
|
||||
pw.print("mPlaySound=");
|
||||
pw.println(mPlaySound);
|
||||
pw.print("mInvalidCharger=");
|
||||
pw.println(mInvalidCharger);
|
||||
pw.print("mShowing=");
|
||||
pw.println(SHOWING_STRINGS[mShowing]);
|
||||
pw.print("mSaverConfirmation=");
|
||||
pw.println(mSaverConfirmation != null ? "not null" : null);
|
||||
pw.print("mSaverEnabledConfirmation=");
|
||||
pw.println(mSaverEnabledConfirmation != null ? "not null" : null);
|
||||
pw.print("mHighTempWarning="); pw.println(mHighTempWarning);
|
||||
pw.print("mHighTempDialog="); pw.println(mHighTempDialog != null ? "not null" : null);
|
||||
pw.print("mHighTempWarning=");
|
||||
pw.println(mHighTempWarning);
|
||||
pw.print("mHighTempDialog=");
|
||||
pw.println(mHighTempDialog != null ? "not null" : null);
|
||||
pw.print("mOverheatAlarmDialog=");
|
||||
pw.println(mOverheatAlarmDialog != null ? "not null" : null);
|
||||
pw.print("mThermalShutdownDialog=");
|
||||
pw.println(mThermalShutdownDialog != null ? "not null" : null);
|
||||
}
|
||||
@@ -370,6 +390,81 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
|
||||
mNoMan.notifyAsUser(TAG_TEMPERATURE, SystemMessage.NOTE_HIGH_TEMP, n, UserHandle.ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* PowerUI detect thermal overheat, notify to popup alarm dialog.
|
||||
* Alarm with beep sound, showing overheat alarm dialog until user click OK or link of help.
|
||||
* Do not auto dismiss even temperature drop.
|
||||
*
|
||||
* @param overheat true if device overheat, temperature >= threshold.
|
||||
* false if device temperature <= threshold tolerance after overheat
|
||||
* alarmed.
|
||||
* @param shouldBeepSound true alarm beep sound until user interactive with device
|
||||
* false showing alarm dialog only
|
||||
*/
|
||||
@Override
|
||||
public void notifyHighTemperatureAlarm(boolean overheat, boolean shouldBeepSound) {
|
||||
// Overheat and non-null dialog are XOR(exclusive or) relationship
|
||||
if (overheat ^ (mOverheatAlarmDialog != null)) {
|
||||
// b/120188825 Since notifyHighTemperatureAlarm() could be triggered by
|
||||
// non-ui thread such as ThermalEventListener.notifyThrottling()
|
||||
mHandler.post(() -> setOverheatAlarmDialogShowing(overheat));
|
||||
setAlarmShouldSound(shouldBeepSound);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Showing overheat alarm dialog until user click OK button or link of help to dismiss
|
||||
*
|
||||
* @param shouldShow whether to show overheat alarm dialog.
|
||||
*/
|
||||
protected void setOverheatAlarmDialogShowing(boolean shouldShow) {
|
||||
if (shouldShow && mOverheatAlarmDialog == null) {
|
||||
OverheatAlarmDialog d = new OverheatAlarmDialog(mContext);
|
||||
d.setCancelable(false);
|
||||
d.setButton(BUTTON_NEGATIVE,
|
||||
mContext.getString(R.string.high_temp_alarm_help_care_steps),
|
||||
(dialogInterface, i) -> {
|
||||
final String contextString = mContext.getString(
|
||||
R.string.high_temp_alarm_help_url);
|
||||
final Intent helpIntent = new Intent();
|
||||
helpIntent.setClassName("com.android.settings",
|
||||
"com.android.settings.HelpTrampoline");
|
||||
helpIntent.putExtra(Intent.EXTRA_TEXT, contextString);
|
||||
Dependency.get(ActivityStarter.class).startActivity(helpIntent,
|
||||
true /* dismissShade */, resultCode -> {
|
||||
mOverheatAlarmDialog = null;
|
||||
});
|
||||
});
|
||||
d.setButton(BUTTON_POSITIVE, mContext.getString(com.android.internal.R.string.ok),
|
||||
(dialogInterface, i) -> mOverheatAlarmDialog = null);
|
||||
d.setOnDismissListener(dialogInterface -> {
|
||||
mOverheatAlarmDialog = null;
|
||||
Events.writeEvent(mContext, Events.EVENT_DISMISS_OVERHEAT_ALARM,
|
||||
Events.DISMISS_REASON_DONE_CLICKED,
|
||||
mKeyguard.isKeyguardLocked());
|
||||
});
|
||||
d.show();
|
||||
Events.writeEvent(mContext, Events.EVENT_SHOW_OVERHEAT_ALARM,
|
||||
Events.SHOW_REASON_OVERHEAD_ALARM_CHANGED,
|
||||
mKeyguard.isKeyguardLocked());
|
||||
mOverheatAlarmDialog = d;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to alarm beep sound when overheat dialog showing.
|
||||
*
|
||||
* @param shouldSound whether to alarm beep sound.
|
||||
*/
|
||||
protected void setAlarmShouldSound(boolean shouldSound) {
|
||||
Log.d(TAG, "setAlarmShouldSound, " + shouldSound);
|
||||
if (shouldSound) {
|
||||
OverheatAlarmController.getInstance(mContext).startAlarm(mContext);
|
||||
} else {
|
||||
OverheatAlarmController.getInstance(mContext).stopAlarm();
|
||||
}
|
||||
}
|
||||
|
||||
private void showHighTemperatureDialog() {
|
||||
if (mHighTempDialog != null) return;
|
||||
final SystemUIDialog d = new SystemUIDialog(mContext);
|
||||
@@ -643,6 +738,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
|
||||
filter.addAction(ACTION_ENABLE_AUTO_SAVER);
|
||||
filter.addAction(ACTION_AUTO_SAVER_NO_THANKS);
|
||||
filter.addAction(ACTION_DISMISS_AUTO_SAVER_SUGGESTION);
|
||||
filter.addAction(Intent.ACTION_ALARM_CHANGED);
|
||||
mContext.registerReceiverAsUser(this, UserHandle.ALL, filter,
|
||||
android.Manifest.permission.DEVICE_POWER, mHandler);
|
||||
}
|
||||
@@ -682,6 +778,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
|
||||
} else if (ACTION_AUTO_SAVER_NO_THANKS.equals(action)) {
|
||||
dismissAutoSaverSuggestion();
|
||||
BatterySaverUtils.suppressAutoBatterySaver(context);
|
||||
} else if (Intent.ACTION_ALARM_CHANGED.equals(action)) {
|
||||
setAlarmShouldSound(false /* mHasUserInteracted */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,12 +54,15 @@ import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
public class PowerUI extends SystemUI {
|
||||
static final String TAG = "PowerUI";
|
||||
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
private static final long TEMPERATURE_INTERVAL = 30 * DateUtils.SECOND_IN_MILLIS;
|
||||
private static final long TEMPERATURE_LOGGING_INTERVAL = DateUtils.HOUR_IN_MILLIS;
|
||||
private static final int TEMPERATURE_OVERHEAT_WARNING = 0;
|
||||
private static final int TEMPERATURE_OVERHEAT_ALARM = 1;
|
||||
private static final int MAX_RECENT_TEMPS = 125; // TEMPERATURE_LOGGING_INTERVAL plus a buffer
|
||||
static final long THREE_HOURS_IN_MILLIS = DateUtils.HOUR_IN_MILLIS * 3;
|
||||
private static final int CHARGE_CYCLE_PERCENT_RESET = 45;
|
||||
@@ -80,15 +83,22 @@ public class PowerUI extends SystemUI {
|
||||
private Estimate mLastEstimate;
|
||||
private boolean mLowWarningShownThisChargeCycle;
|
||||
private boolean mSevereWarningShownThisChargeCycle;
|
||||
private boolean mEnableTemperatureWarning;
|
||||
private boolean mEnableTemperatureAlarm;
|
||||
private boolean mIsOverheatAlarming;
|
||||
|
||||
private int mLowBatteryAlertCloseLevel;
|
||||
private final int[] mLowBatteryReminderLevels = new int[2];
|
||||
|
||||
private long mScreenOffTime = -1;
|
||||
|
||||
private float mThresholdTemp;
|
||||
private float[] mRecentTemps = new float[MAX_RECENT_TEMPS];
|
||||
private int mNumTemps;
|
||||
private float mThresholdWarningTemp;
|
||||
private float mThresholdAlarmTemp;
|
||||
private float mThresholdAlarmTempTolerance;
|
||||
private float[] mRecentSkinTemps = new float[MAX_RECENT_TEMPS];
|
||||
private float[] mRecentAlarmTemps = new float[MAX_RECENT_TEMPS];
|
||||
private int mWarningNumTemps;
|
||||
private int mAlarmNumTemps;
|
||||
private long mNextLogTime;
|
||||
private IThermalService mThermalService;
|
||||
|
||||
@@ -98,7 +108,7 @@ public class PowerUI extends SystemUI {
|
||||
// by using the same instance (method references are not guaranteed to be the same object
|
||||
// We create a method reference here so that we are guaranteed that we can remove a callback
|
||||
// each time they are created).
|
||||
private final Runnable mUpdateTempCallback = this::updateTemperatureWarning;
|
||||
private final Runnable mUpdateTempCallback = this::updateTemperature;
|
||||
|
||||
public void start() {
|
||||
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
|
||||
@@ -126,7 +136,7 @@ public class PowerUI extends SystemUI {
|
||||
// to the temperature being too high.
|
||||
showThermalShutdownDialog();
|
||||
|
||||
initTemperatureWarning();
|
||||
initTemperature();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -135,7 +145,7 @@ public class PowerUI extends SystemUI {
|
||||
|
||||
// Safe to modify mLastConfiguration here as it's only updated by the main thread (here).
|
||||
if ((mLastConfiguration.updateFrom(newConfig) & mask) != 0) {
|
||||
mHandler.post(this::initTemperatureWarning);
|
||||
mHandler.post(this::initTemperature);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,6 +203,7 @@ public class PowerUI extends SystemUI {
|
||||
filter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||
filter.addAction(Intent.ACTION_SCREEN_ON);
|
||||
filter.addAction(Intent.ACTION_USER_SWITCHED);
|
||||
filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
|
||||
mContext.registerReceiver(this, filter, null, mHandler);
|
||||
}
|
||||
|
||||
@@ -258,6 +269,8 @@ public class PowerUI extends SystemUI {
|
||||
mScreenOffTime = -1;
|
||||
} else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
|
||||
mWarnings.userSwitched();
|
||||
} else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
|
||||
updateTemperature();
|
||||
} else {
|
||||
Slog.w(TAG, "unknown intent: " + intent);
|
||||
}
|
||||
@@ -364,18 +377,51 @@ public class PowerUI extends SystemUI {
|
||||
return canShowWarning || canShowSevereWarning;
|
||||
}
|
||||
|
||||
private void initTemperatureWarning() {
|
||||
ContentResolver resolver = mContext.getContentResolver();
|
||||
Resources resources = mContext.getResources();
|
||||
if (Settings.Global.getInt(resolver, Settings.Global.SHOW_TEMPERATURE_WARNING,
|
||||
resources.getInteger(R.integer.config_showTemperatureWarning)) == 0) {
|
||||
private void initTemperature() {
|
||||
initTemperatureWarning();
|
||||
initTemperatureAlarm();
|
||||
if (mEnableTemperatureWarning || mEnableTemperatureAlarm) {
|
||||
bindThermalService();
|
||||
}
|
||||
}
|
||||
|
||||
private void initTemperatureAlarm() {
|
||||
mEnableTemperatureAlarm = mContext.getResources().getInteger(
|
||||
R.integer.config_showTemperatureAlarm) != 0;
|
||||
if (!mEnableTemperatureAlarm) {
|
||||
return;
|
||||
}
|
||||
|
||||
mThresholdTemp = Settings.Global.getFloat(resolver, Settings.Global.WARNING_TEMPERATURE,
|
||||
float[] throttlingTemps = mHardwarePropertiesManager.getDeviceTemperatures(
|
||||
HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN,
|
||||
HardwarePropertiesManager.TEMPERATURE_THROTTLING);
|
||||
if (throttlingTemps == null || throttlingTemps.length < TEMPERATURE_OVERHEAT_ALARM + 1) {
|
||||
mThresholdAlarmTemp = mContext.getResources().getInteger(
|
||||
R.integer.config_alarmTemperature);
|
||||
} else {
|
||||
mThresholdAlarmTemp = throttlingTemps[TEMPERATURE_OVERHEAT_ALARM];
|
||||
}
|
||||
mThresholdAlarmTempTolerance = mThresholdAlarmTemp - mContext.getResources().getInteger(
|
||||
R.integer.config_alarmTemperatureTolerance);
|
||||
Log.d(TAG, "mThresholdAlarmTemp=" + mThresholdAlarmTemp + ", mThresholdAlarmTempTolerance="
|
||||
+ mThresholdAlarmTempTolerance);
|
||||
}
|
||||
|
||||
private void initTemperatureWarning() {
|
||||
ContentResolver resolver = mContext.getContentResolver();
|
||||
Resources resources = mContext.getResources();
|
||||
mEnableTemperatureWarning = Settings.Global.getInt(resolver,
|
||||
Settings.Global.SHOW_TEMPERATURE_WARNING,
|
||||
resources.getInteger(R.integer.config_showTemperatureWarning)) != 0;
|
||||
if (!mEnableTemperatureWarning) {
|
||||
return;
|
||||
}
|
||||
|
||||
mThresholdWarningTemp = Settings.Global.getFloat(resolver,
|
||||
Settings.Global.WARNING_TEMPERATURE,
|
||||
resources.getInteger(R.integer.config_warningTemperature));
|
||||
|
||||
if (mThresholdTemp < 0f) {
|
||||
if (mThresholdWarningTemp < 0f) {
|
||||
// Get the shutdown temperature, adjust for warning tolerance.
|
||||
float[] throttlingTemps = mHardwarePropertiesManager.getDeviceTemperatures(
|
||||
HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN,
|
||||
@@ -385,10 +431,12 @@ public class PowerUI extends SystemUI {
|
||||
|| throttlingTemps[0] == HardwarePropertiesManager.UNDEFINED_TEMPERATURE) {
|
||||
return;
|
||||
}
|
||||
mThresholdTemp = throttlingTemps[0] -
|
||||
resources.getInteger(R.integer.config_warningTemperatureTolerance);
|
||||
mThresholdWarningTemp = throttlingTemps[0] - resources.getInteger(
|
||||
R.integer.config_warningTemperatureTolerance);
|
||||
}
|
||||
}
|
||||
|
||||
private void bindThermalService() {
|
||||
if (mThermalService == null) {
|
||||
// Enable push notifications of throttling from vendor thermal
|
||||
// management subsystem via thermalservice, in addition to our
|
||||
@@ -416,7 +464,7 @@ public class PowerUI extends SystemUI {
|
||||
mHandler.removeCallbacks(mUpdateTempCallback);
|
||||
|
||||
// We have passed all of the checks, start checking the temp
|
||||
updateTemperatureWarning();
|
||||
updateTemperature();
|
||||
}
|
||||
|
||||
private void showThermalShutdownDialog() {
|
||||
@@ -426,38 +474,110 @@ public class PowerUI extends SystemUI {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update temperature depend on config, there are type types of messages by design
|
||||
* TEMPERATURE_OVERHEAT_WARNING Send generic notification to notify user
|
||||
* TEMPERATURE_OVERHEAT_ALARM popup emergency Dialog for user
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected void updateTemperatureWarning() {
|
||||
protected void updateTemperature() {
|
||||
if (!mEnableTemperatureWarning && !mEnableTemperatureAlarm) {
|
||||
return;
|
||||
}
|
||||
|
||||
float[] temps = mHardwarePropertiesManager.getDeviceTemperatures(
|
||||
HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN,
|
||||
HardwarePropertiesManager.TEMPERATURE_CURRENT);
|
||||
if (temps == null) {
|
||||
Log.e(TAG, "Can't query current temperature value from HardProps SKIN type");
|
||||
return;
|
||||
}
|
||||
|
||||
final float[] temps_compat;
|
||||
if (temps.length < TEMPERATURE_OVERHEAT_ALARM + 1) {
|
||||
temps_compat = new float[] { temps[0], temps[0] };
|
||||
} else {
|
||||
temps_compat = temps;
|
||||
}
|
||||
|
||||
if (mEnableTemperatureWarning) {
|
||||
updateTemperatureWarning(temps_compat);
|
||||
logTemperatureStats(TEMPERATURE_OVERHEAT_WARNING);
|
||||
}
|
||||
|
||||
if (mEnableTemperatureAlarm) {
|
||||
updateTemperatureAlarm(temps_compat);
|
||||
logTemperatureStats(TEMPERATURE_OVERHEAT_ALARM);
|
||||
}
|
||||
|
||||
mHandler.postDelayed(mUpdateTempCallback, TEMPERATURE_INTERVAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update legacy overheat warning notification from skin temperatures
|
||||
*
|
||||
* @param temps the array include two types of value from DEVICE_TEMPERATURE_SKIN
|
||||
* this function only obtain the value of temps[TEMPERATURE_OVERHEAT_WARNING]
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected void updateTemperatureWarning(float[] temps) {
|
||||
if (temps.length != 0) {
|
||||
float temp = temps[0];
|
||||
mRecentTemps[mNumTemps++] = temp;
|
||||
float temp = temps[TEMPERATURE_OVERHEAT_WARNING];
|
||||
mRecentSkinTemps[mWarningNumTemps++] = temp;
|
||||
|
||||
StatusBar statusBar = getComponent(StatusBar.class);
|
||||
if (statusBar != null && !statusBar.isDeviceInVrMode()
|
||||
&& temp >= mThresholdTemp) {
|
||||
logAtTemperatureThreshold(temp);
|
||||
&& temp >= mThresholdWarningTemp) {
|
||||
logAtTemperatureThreshold(TEMPERATURE_OVERHEAT_WARNING, temp,
|
||||
mThresholdWarningTemp);
|
||||
mWarnings.showHighTemperatureWarning();
|
||||
} else {
|
||||
mWarnings.dismissHighTemperatureWarning();
|
||||
}
|
||||
}
|
||||
|
||||
logTemperatureStats();
|
||||
|
||||
mHandler.postDelayed(mUpdateTempCallback, TEMPERATURE_INTERVAL);
|
||||
}
|
||||
|
||||
private void logAtTemperatureThreshold(float temp) {
|
||||
/**
|
||||
* Update overheat alarm from skin temperatures
|
||||
* OEM can config alarm with beep sound by config_alarmTemperatureBeepSound
|
||||
*
|
||||
* @param temps the array include two types of value from DEVICE_TEMPERATURE_SKIN
|
||||
* this function only obtain the value of temps[TEMPERATURE_OVERHEAT_ALARM]
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected void updateTemperatureAlarm(float[] temps) {
|
||||
if (temps.length != 0) {
|
||||
final float temp = temps[TEMPERATURE_OVERHEAT_ALARM];
|
||||
final boolean shouldBeepSound = mContext.getResources().getBoolean(
|
||||
R.bool.config_alarmTemperatureBeepSound);
|
||||
mRecentAlarmTemps[mAlarmNumTemps++] = temp;
|
||||
if (temp >= mThresholdAlarmTemp && !mIsOverheatAlarming) {
|
||||
mWarnings.notifyHighTemperatureAlarm(true /* overheat */, shouldBeepSound);
|
||||
mIsOverheatAlarming = true;
|
||||
} else if (temp <= mThresholdAlarmTempTolerance && mIsOverheatAlarming) {
|
||||
mWarnings.notifyHighTemperatureAlarm(false /* overheat */, false /* beepSound */);
|
||||
mIsOverheatAlarming = false;
|
||||
}
|
||||
logAtTemperatureThreshold(TEMPERATURE_OVERHEAT_ALARM, temp, mThresholdAlarmTemp);
|
||||
}
|
||||
}
|
||||
|
||||
private void logAtTemperatureThreshold(int type, float temp, float thresholdTemp) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("currentTemp=").append(temp)
|
||||
.append(",thresholdTemp=").append(mThresholdTemp)
|
||||
.append(",overheatType=").append(type)
|
||||
.append(",isOverheatAlarm=").append(mIsOverheatAlarming)
|
||||
.append(",thresholdTemp=").append(thresholdTemp)
|
||||
.append(",batteryStatus=").append(mBatteryStatus)
|
||||
.append(",recentTemps=");
|
||||
for (int i = 0; i < mNumTemps; i++) {
|
||||
sb.append(mRecentTemps[i]).append(',');
|
||||
if (type == TEMPERATURE_OVERHEAT_WARNING) {
|
||||
for (int i = 0; i < mWarningNumTemps; i++) {
|
||||
sb.append(mRecentSkinTemps[i]).append(',');
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < mAlarmNumTemps; i++) {
|
||||
sb.append(mRecentAlarmTemps[i]).append(',');
|
||||
}
|
||||
}
|
||||
Slog.i(TAG, sb.toString());
|
||||
}
|
||||
@@ -466,16 +586,20 @@ public class PowerUI extends SystemUI {
|
||||
* Calculates and logs min, max, and average
|
||||
* {@link HardwarePropertiesManager#DEVICE_TEMPERATURE_SKIN} over the past
|
||||
* {@link #TEMPERATURE_LOGGING_INTERVAL}.
|
||||
* @param type TEMPERATURE_OVERHEAT_WARNING Send generic notification to notify user
|
||||
* TEMPERATURE_OVERHEAT_ALARM Popup emergency Dialog for user
|
||||
*/
|
||||
private void logTemperatureStats() {
|
||||
if (mNextLogTime > System.currentTimeMillis() && mNumTemps != MAX_RECENT_TEMPS) {
|
||||
private void logTemperatureStats(int type) {
|
||||
int numTemp = type == TEMPERATURE_OVERHEAT_ALARM ? mAlarmNumTemps : mWarningNumTemps;
|
||||
if (mNextLogTime > System.currentTimeMillis() && numTemp != MAX_RECENT_TEMPS) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mNumTemps > 0) {
|
||||
float sum = mRecentTemps[0], min = mRecentTemps[0], max = mRecentTemps[0];
|
||||
for (int i = 1; i < mNumTemps; i++) {
|
||||
float temp = mRecentTemps[i];
|
||||
float[] recentTemps =
|
||||
type == TEMPERATURE_OVERHEAT_ALARM ? mRecentAlarmTemps : mRecentSkinTemps;
|
||||
if (numTemp > 0) {
|
||||
float sum = recentTemps[0], min = recentTemps[0], max = recentTemps[0];
|
||||
for (int i = 1; i < numTemp; i++) {
|
||||
float temp = recentTemps[i];
|
||||
sum += temp;
|
||||
if (temp > max) {
|
||||
max = temp;
|
||||
@@ -485,14 +609,22 @@ public class PowerUI extends SystemUI {
|
||||
}
|
||||
}
|
||||
|
||||
float avg = sum / mNumTemps;
|
||||
Slog.i(TAG, "avg=" + avg + ",min=" + min + ",max=" + max);
|
||||
MetricsLogger.histogram(mContext, "device_skin_temp_avg", (int) avg);
|
||||
MetricsLogger.histogram(mContext, "device_skin_temp_min", (int) min);
|
||||
MetricsLogger.histogram(mContext, "device_skin_temp_max", (int) max);
|
||||
float avg = sum / numTemp;
|
||||
Slog.i(TAG, "Type=" + type + ",avg=" + avg + ",min=" + min + ",max=" + max);
|
||||
String t = type == TEMPERATURE_OVERHEAT_WARNING ? "skin" : "alarm";
|
||||
MetricsLogger.histogram(mContext,
|
||||
String.format(Locale.ENGLISH, "device_%1$s_temp_avg", t), (int) avg);
|
||||
MetricsLogger.histogram(mContext,
|
||||
String.format(Locale.ENGLISH, "device_%1$s_temp_min", t), (int) min);
|
||||
MetricsLogger.histogram(mContext,
|
||||
String.format(Locale.ENGLISH, "device_%1$s_temp_max", t), (int) max);
|
||||
}
|
||||
setNextLogTime();
|
||||
mNumTemps = 0;
|
||||
if (type == TEMPERATURE_OVERHEAT_ALARM) {
|
||||
mAlarmNumTemps = 0;
|
||||
} else {
|
||||
mWarningNumTemps = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void setNextLogTime() {
|
||||
@@ -525,8 +657,10 @@ public class PowerUI extends SystemUI {
|
||||
Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
|
||||
pw.print("bucket: ");
|
||||
pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
|
||||
pw.print("mThresholdTemp=");
|
||||
pw.println(Float.toString(mThresholdTemp));
|
||||
pw.print("mThresholdWarningTemp=");
|
||||
pw.println(Float.toString(mThresholdWarningTemp));
|
||||
pw.print("mThresholdAlarmTemp=");
|
||||
pw.println(Float.toString(mThresholdAlarmTemp));
|
||||
pw.print("mNextLogTime=");
|
||||
pw.println(Long.toString(mNextLogTime));
|
||||
mWarnings.dump(pw);
|
||||
@@ -534,18 +668,41 @@ public class PowerUI extends SystemUI {
|
||||
|
||||
public interface WarningsUI {
|
||||
void update(int batteryLevel, int bucket, long screenOffTime);
|
||||
|
||||
void updateEstimate(Estimate estimate);
|
||||
|
||||
void updateThresholds(long lowThreshold, long severeThreshold);
|
||||
|
||||
void dismissLowBatteryWarning();
|
||||
|
||||
void showLowBatteryWarning(boolean playSound);
|
||||
|
||||
void dismissInvalidChargerWarning();
|
||||
|
||||
void showInvalidChargerWarning();
|
||||
|
||||
void updateLowBatteryWarning();
|
||||
|
||||
boolean isInvalidChargerWarningShowing();
|
||||
|
||||
void dismissHighTemperatureWarning();
|
||||
|
||||
void showHighTemperatureWarning();
|
||||
|
||||
/**
|
||||
* PowerUI detect thermal overheat, notify to popup alert dialog strongly.
|
||||
* Alarm with beep sound with un-dismissible dialog until device cool down below threshold,
|
||||
* then popup another dismissible dialog to user.
|
||||
*
|
||||
* @param overheat whether device temperature over threshold
|
||||
* @param beepSound should beep sound once overheat
|
||||
*/
|
||||
void notifyHighTemperatureAlarm(boolean overheat, boolean beepSound);
|
||||
|
||||
void showThermalShutdownWarning();
|
||||
|
||||
void dump(PrintWriter pw);
|
||||
|
||||
void userSwitched();
|
||||
}
|
||||
|
||||
@@ -554,9 +711,9 @@ public class PowerUI extends SystemUI {
|
||||
@Override public void notifyThrottling(boolean isThrottling, Temperature temp) {
|
||||
// Trigger an update of the temperature warning. Only one
|
||||
// callback can be enabled at a time, so remove any existing
|
||||
// callback; updateTemperatureWarning will schedule another one.
|
||||
// callback; updateTemperature will schedule another one.
|
||||
mHandler.removeCallbacks(mUpdateTempCallback);
|
||||
updateTemperatureWarning();
|
||||
updateTemperature();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,8 @@ public class Events {
|
||||
public static final int EVENT_TOUCH_LEVEL_DONE = 16; // (stream|int) (level|bool)
|
||||
public static final int EVENT_ZEN_CONFIG_CHANGED = 17; // (allow/disallow|string)
|
||||
public static final int EVENT_RINGER_TOGGLE = 18; // (ringer_mode)
|
||||
public static final int EVENT_SHOW_OVERHEAT_ALARM = 19; // (reason|int) (keyguard|bool)
|
||||
public static final int EVENT_DISMISS_OVERHEAT_ALARM = 20; // (reason|int) (keyguard|bool)
|
||||
|
||||
private static final String[] EVENT_TAGS = {
|
||||
"show_dialog",
|
||||
@@ -73,7 +75,9 @@ public class Events {
|
||||
"mute_changed",
|
||||
"touch_level_done",
|
||||
"zen_mode_config_changed",
|
||||
"ringer_toggle"
|
||||
"ringer_toggle",
|
||||
"show_overheat_alarm",
|
||||
"dismiss_overheat_alarm"
|
||||
};
|
||||
|
||||
public static final int DISMISS_REASON_UNKNOWN = 0;
|
||||
@@ -100,10 +104,12 @@ public class Events {
|
||||
public static final int SHOW_REASON_UNKNOWN = 0;
|
||||
public static final int SHOW_REASON_VOLUME_CHANGED = 1;
|
||||
public static final int SHOW_REASON_REMOTE_VOLUME_CHANGED = 2;
|
||||
public static final int SHOW_REASON_OVERHEAD_ALARM_CHANGED = 3;
|
||||
public static final String[] SHOW_REASONS = {
|
||||
"unknown",
|
||||
"volume_changed",
|
||||
"remote_volume_changed"
|
||||
"remote_volume_changed",
|
||||
"overheat_alarm_changed"
|
||||
};
|
||||
|
||||
public static final int ICON_STATE_UNKNOWN = 0;
|
||||
@@ -181,6 +187,19 @@ public class Events {
|
||||
case EVENT_SUPPRESSOR_CHANGED:
|
||||
sb.append(list[0]).append(' ').append(list[1]);
|
||||
break;
|
||||
case EVENT_SHOW_OVERHEAT_ALARM:
|
||||
MetricsLogger.visible(context, MetricsEvent.POWER_OVERHEAT_ALARM);
|
||||
MetricsLogger.histogram(context, "show_overheat_alarm",
|
||||
(Boolean) list[1] ? 1 : 0);
|
||||
sb.append(SHOW_REASONS[(Integer) list[0]]).append(" keyguard=").append(list[1]);
|
||||
break;
|
||||
case EVENT_DISMISS_OVERHEAT_ALARM:
|
||||
MetricsLogger.hidden(context, MetricsEvent.POWER_OVERHEAT_ALARM);
|
||||
MetricsLogger.histogram(context, "dismiss_overheat_alarm",
|
||||
(Boolean) list[1] ? 1 : 0);
|
||||
sb.append(DISMISS_REASONS[(Integer) list[0]]).append(" keyguard=").append(
|
||||
list[1]);
|
||||
break;
|
||||
default:
|
||||
sb.append(Arrays.asList(list));
|
||||
break;
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
<uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
|
||||
<uses-permission android:name="android.permission.NETWORK_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<application android:debuggable="true">
|
||||
<uses-library android:name="android.test.runner" />
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.power;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWithLooper
|
||||
@SmallTest
|
||||
public class OverheatAlarmControllerTest extends SysuiTestCase{
|
||||
OverheatAlarmController mOverheatAlarmController;
|
||||
OverheatAlarmController mSpyOverheatAlarmController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mOverheatAlarmController = OverheatAlarmController.getInstance(mContext);
|
||||
mSpyOverheatAlarmController = spy(mOverheatAlarmController);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
mSpyOverheatAlarmController.stopAlarm();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInstance() {
|
||||
assertThat(mOverheatAlarmController).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartAlarm_PlaySound() {
|
||||
mSpyOverheatAlarmController.startAlarm(mContext);
|
||||
verify(mSpyOverheatAlarmController).playSound(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartAlarm_InitVibrate() {
|
||||
mSpyOverheatAlarmController.startAlarm(mContext);
|
||||
verify(mSpyOverheatAlarmController).startVibrate();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartAlarm_StartVibrate() {
|
||||
mSpyOverheatAlarmController.startAlarm(mContext);
|
||||
verify(mSpyOverheatAlarmController).performVibrate();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopAlarm_StopPlayer() {
|
||||
mSpyOverheatAlarmController.stopAlarm();
|
||||
verify(mSpyOverheatAlarmController).stopPlayer();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopAlarm_StopVibrate() {
|
||||
mSpyOverheatAlarmController.stopAlarm();
|
||||
verify(mSpyOverheatAlarmController).stopVibrate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.power;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
@SmallTest
|
||||
public class OverheatAlarmDialogTest extends SysuiTestCase {
|
||||
|
||||
private OverheatAlarmDialog mDialog, mSpyDialog;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mDialog = new OverheatAlarmDialog(mContext);
|
||||
mSpyDialog = spy(mDialog);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
mSpyDialog = mDialog = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlagShowForAllUsers() {
|
||||
assertThat((mDialog.getWindow().getAttributes().privateFlags
|
||||
& WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS) != 0).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlagShowWhenLocked() {
|
||||
assertThat((mDialog.getWindow().getAttributes().flags
|
||||
& WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED) != 0).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlagTurnScreenOn() {
|
||||
assertThat((mDialog.getWindow().getAttributes().flags
|
||||
& WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) != 0).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlagKeepScreenOn() {
|
||||
assertThat((mDialog.getWindow().getAttributes().flags
|
||||
& WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTouchOutsideDialog_NotifyAlarmBeepSoundIntent_ShouldStopBeepSound() {
|
||||
final long currentTime = SystemClock.uptimeMillis();
|
||||
mSpyDialog.show();
|
||||
MotionEvent ev = createMotionEvent(MotionEvent.ACTION_DOWN, currentTime, 0, 0);
|
||||
mSpyDialog.dispatchTouchEvent(ev);
|
||||
|
||||
verify(mSpyDialog, atLeastOnce()).notifyAlarmBeepSoundChange();
|
||||
mSpyDialog.dismiss();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPressBackKey_NotifyAlarmBeepSoundIntent_ShouldStopBeepSound() {
|
||||
mSpyDialog.show();
|
||||
KeyEvent ev = new KeyEvent(0, 0, MotionEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK, 0,
|
||||
0);
|
||||
mSpyDialog.dispatchKeyEvent(ev);
|
||||
|
||||
verify(mSpyDialog, atLeastOnce()).notifyAlarmBeepSoundChange();
|
||||
mSpyDialog.dismiss();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPressVolumeUp_NotifyAlarmBeepSoundIntent_ShouldStopBeepSound() {
|
||||
mSpyDialog.show();
|
||||
KeyEvent ev = new KeyEvent(0, 0, MotionEvent.ACTION_DOWN,
|
||||
KeyEvent.KEYCODE_VOLUME_UP, 0, 0);
|
||||
mSpyDialog.dispatchKeyEvent(ev);
|
||||
|
||||
verify(mSpyDialog, atLeastOnce()).notifyAlarmBeepSoundChange();
|
||||
mSpyDialog.dismiss();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPressVolumeDown_NotifyAlarmBeepSoundIntent_ShouldStopBeepSound() {
|
||||
mSpyDialog.show();
|
||||
KeyEvent ev = new KeyEvent(0, 0, MotionEvent.ACTION_DOWN,
|
||||
KeyEvent.KEYCODE_VOLUME_DOWN, 0, 0);
|
||||
mSpyDialog.dispatchKeyEvent(ev);
|
||||
|
||||
verify(mSpyDialog, atLeastOnce()).notifyAlarmBeepSoundChange();
|
||||
mSpyDialog.dismiss();
|
||||
}
|
||||
|
||||
private MotionEvent createMotionEvent(int action, long eventTime, float x, float y) {
|
||||
return MotionEvent.obtain(0, eventTime, action, x, y, 0);
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,8 @@
|
||||
|
||||
package com.android.systemui.power;
|
||||
|
||||
import static android.test.MoreAsserts.assertNotEqual;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
@@ -26,6 +25,8 @@ import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@@ -38,7 +39,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.util.NotificationChannels;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -51,13 +52,22 @@ public class PowerNotificationWarningsTest extends SysuiTestCase {
|
||||
public static final String FORMATTED_45M = "0h 45m";
|
||||
public static final String FORMATTED_HOUR = "1h 0m";
|
||||
private final NotificationManager mMockNotificationManager = mock(NotificationManager.class);
|
||||
private PowerNotificationWarnings mPowerNotificationWarnings;
|
||||
private PowerNotificationWarnings mPowerNotificationWarnings, mSpyPowerNotificationWarnings;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
// Test Instance.
|
||||
mContext.addMockSystemService(NotificationManager.class, mMockNotificationManager);
|
||||
mPowerNotificationWarnings = new PowerNotificationWarnings(mContext);
|
||||
mSpyPowerNotificationWarnings = spy(mPowerNotificationWarnings);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
if (mSpyPowerNotificationWarnings.mOverheatAlarmDialog != null) {
|
||||
mSpyPowerNotificationWarnings.mOverheatAlarmDialog.dismiss();
|
||||
mSpyPowerNotificationWarnings.mOverheatAlarmDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -151,4 +161,146 @@ public class PowerNotificationWarningsTest extends SysuiTestCase {
|
||||
verify(mMockNotificationManager, times(1)).cancelAsUser(anyString(),
|
||||
eq(SystemMessage.NOTE_THERMAL_SHUTDOWN), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetOverheatAlarmDialog_Overheat_ShouldShowing() {
|
||||
final boolean overheat = true;
|
||||
final boolean shouldBeepSound = false;
|
||||
mContext.getMainThreadHandler().post(
|
||||
() -> mSpyPowerNotificationWarnings.notifyHighTemperatureAlarm(overheat,
|
||||
shouldBeepSound));
|
||||
waitForIdleSync();
|
||||
verify(mSpyPowerNotificationWarnings, times(1)).setOverheatAlarmDialogShowing(overheat);
|
||||
verify(mSpyPowerNotificationWarnings, times(1)).setAlarmShouldSound(shouldBeepSound);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetOverheatAlarmDialog_Overheat_ShouldShowingWithBeepSound() {
|
||||
final boolean overheat = true;
|
||||
final boolean shouldBeepSound = true;
|
||||
mContext.getMainThreadHandler().post(
|
||||
() -> mSpyPowerNotificationWarnings.notifyHighTemperatureAlarm(overheat,
|
||||
shouldBeepSound));
|
||||
waitForIdleSync();
|
||||
verify(mSpyPowerNotificationWarnings, times(1)).setOverheatAlarmDialogShowing(overheat);
|
||||
verify(mSpyPowerNotificationWarnings, times(1)).setAlarmShouldSound(shouldBeepSound);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetOverheatAlarmDialog_NotOverheat_ShouldNotShowing() {
|
||||
final boolean overheat = false;
|
||||
final boolean shouldBeepSound = false;
|
||||
mContext.getMainThreadHandler().post(
|
||||
() -> mSpyPowerNotificationWarnings.notifyHighTemperatureAlarm(overheat,
|
||||
shouldBeepSound));
|
||||
waitForIdleSync();
|
||||
verify(mSpyPowerNotificationWarnings, never()).setOverheatAlarmDialogShowing(overheat);
|
||||
verify(mSpyPowerNotificationWarnings, never()).setAlarmShouldSound(shouldBeepSound);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetOverheatAlarmDialog_NotOverheat_ShouldNotAlarmBeepSound() {
|
||||
final boolean overheat = false;
|
||||
final boolean configBeepSound = true;
|
||||
mContext.getMainThreadHandler().post(
|
||||
() -> mSpyPowerNotificationWarnings.notifyHighTemperatureAlarm(overheat,
|
||||
configBeepSound));
|
||||
waitForIdleSync();
|
||||
verify(mSpyPowerNotificationWarnings, never()).setOverheatAlarmDialogShowing(overheat);
|
||||
verify(mSpyPowerNotificationWarnings, never()).setAlarmShouldSound(configBeepSound);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAlarmShouldSound_OverheatDrop_ShouldNotSound() {
|
||||
final boolean overheat = true;
|
||||
final boolean shouldBeepSound = true;
|
||||
mContext.getMainThreadHandler().post(
|
||||
() -> mSpyPowerNotificationWarnings.notifyHighTemperatureAlarm(overheat,
|
||||
shouldBeepSound));
|
||||
waitForIdleSync();
|
||||
// First time overheat, show overheat alarm dialog with alarm beep sound
|
||||
verify(mSpyPowerNotificationWarnings, times(1)).setOverheatAlarmDialogShowing(overheat);
|
||||
verify(mSpyPowerNotificationWarnings, times(1)).setAlarmShouldSound(shouldBeepSound);
|
||||
|
||||
// After disconnected cable or temperature drop
|
||||
mContext.getMainThreadHandler().post(
|
||||
() -> mSpyPowerNotificationWarnings.notifyHighTemperatureAlarm(!overheat,
|
||||
!shouldBeepSound));
|
||||
waitForIdleSync();
|
||||
verify(mSpyPowerNotificationWarnings, times(1)).setOverheatAlarmDialogShowing(!overheat);
|
||||
verify(mSpyPowerNotificationWarnings, times(1)).setAlarmShouldSound(!shouldBeepSound);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAlarmShouldSound_Overheat_Twice_ShouldShowOverheatDialogAgain() {
|
||||
final boolean overheat = true;
|
||||
final boolean shouldBeepSound = true;
|
||||
// First time overheat, show mAlarmDialog and alarm beep sound
|
||||
mContext.getMainThreadHandler().post(
|
||||
() -> mSpyPowerNotificationWarnings.notifyHighTemperatureAlarm(overheat,
|
||||
shouldBeepSound));
|
||||
waitForIdleSync();
|
||||
verify(mSpyPowerNotificationWarnings, times(1)).setOverheatAlarmDialogShowing(overheat);
|
||||
verify(mSpyPowerNotificationWarnings, times(1)).setAlarmShouldSound(shouldBeepSound);
|
||||
|
||||
// After disconnected cable or temperature drop, stop beep sound
|
||||
mContext.getMainThreadHandler().post(
|
||||
() -> mSpyPowerNotificationWarnings.notifyHighTemperatureAlarm(!overheat,
|
||||
!shouldBeepSound));
|
||||
waitForIdleSync();
|
||||
verify(mSpyPowerNotificationWarnings, times(1)).setOverheatAlarmDialogShowing(!overheat);
|
||||
verify(mSpyPowerNotificationWarnings, times(1)).setAlarmShouldSound(!shouldBeepSound);
|
||||
|
||||
// Overheat again, ensure the previous dialog do not auto-dismiss
|
||||
mContext.getMainThreadHandler().post(
|
||||
() -> mSpyPowerNotificationWarnings.notifyHighTemperatureAlarm(overheat,
|
||||
shouldBeepSound));
|
||||
waitForIdleSync();
|
||||
verify(mSpyPowerNotificationWarnings, times(1)).setOverheatAlarmDialogShowing(overheat);
|
||||
verify(mSpyPowerNotificationWarnings, times(1)).setAlarmShouldSound(shouldBeepSound);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverheatAlarmDialogShowing() {
|
||||
final boolean overheat = true;
|
||||
final boolean shouldBeepSound = false;
|
||||
mContext.getMainThreadHandler().post(
|
||||
() -> mSpyPowerNotificationWarnings.notifyHighTemperatureAlarm(overheat,
|
||||
shouldBeepSound));
|
||||
waitForIdleSync();
|
||||
assertThat(mSpyPowerNotificationWarnings.mOverheatAlarmDialog).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverheatAlarmDialogShowingWithBeepSound() {
|
||||
final boolean overheat = true;
|
||||
final boolean shouldBeepSound = true;
|
||||
mContext.getMainThreadHandler().post(
|
||||
() -> mSpyPowerNotificationWarnings.notifyHighTemperatureAlarm(overheat,
|
||||
shouldBeepSound));
|
||||
waitForIdleSync();
|
||||
assertThat(mSpyPowerNotificationWarnings.mOverheatAlarmDialog).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverheatAlarmDialogNotShowing() {
|
||||
final boolean overheat = false;
|
||||
final boolean shouldBeepSound = false;
|
||||
mContext.getMainThreadHandler().post(
|
||||
() -> mSpyPowerNotificationWarnings.notifyHighTemperatureAlarm(overheat,
|
||||
shouldBeepSound));
|
||||
waitForIdleSync();
|
||||
assertThat(mSpyPowerNotificationWarnings.mOverheatAlarmDialog).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverheatAlarmDialogNotShowingWithBeepSound() {
|
||||
final boolean overheat = false;
|
||||
final boolean shouldBeepSound = true;
|
||||
mContext.getMainThreadHandler().post(
|
||||
() -> mSpyPowerNotificationWarnings.notifyHighTemperatureAlarm(overheat,
|
||||
shouldBeepSound));
|
||||
waitForIdleSync();
|
||||
assertThat(mSpyPowerNotificationWarnings.mOverheatAlarmDialog).isNull();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.android.systemui.power;
|
||||
import static android.os.HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN;
|
||||
import static android.os.HardwarePropertiesManager.TEMPERATURE_CURRENT;
|
||||
import static android.os.HardwarePropertiesManager.TEMPERATURE_SHUTDOWN;
|
||||
import static android.os.HardwarePropertiesManager.TEMPERATURE_THROTTLING;
|
||||
import static android.provider.Settings.Global.SHOW_TEMPERATURE_WARNING;
|
||||
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
@@ -69,6 +70,7 @@ public class PowerUITest extends SysuiTestCase {
|
||||
private static final long ABOVE_CHARGE_CYCLE_THRESHOLD = Duration.ofHours(8).toMillis();
|
||||
private static final int OLD_BATTERY_LEVEL_NINE = 9;
|
||||
private static final int OLD_BATTERY_LEVEL_10 = 10;
|
||||
private static final int DEFAULT_OVERHEAT_ALARM_THRESHOLD = 58;
|
||||
private HardwarePropertiesManager mHardProps;
|
||||
private WarningsUI mMockWarnings;
|
||||
private PowerUI mPowerUI;
|
||||
@@ -86,6 +88,7 @@ public class PowerUITest extends SysuiTestCase {
|
||||
mContext.addMockSystemService(Context.HARDWARE_PROPERTIES_SERVICE, mHardProps);
|
||||
mContext.addMockSystemService(Context.POWER_SERVICE, mPowerManager);
|
||||
|
||||
setUnderThreshold();
|
||||
createPowerUi();
|
||||
}
|
||||
|
||||
@@ -153,10 +156,94 @@ public class PowerUITest extends SysuiTestCase {
|
||||
verify(mMockWarnings, never()).showHighTemperatureWarning();
|
||||
|
||||
setCurrentTemp(56); // Above threshold.
|
||||
mPowerUI.updateTemperatureWarning();
|
||||
mPowerUI.updateTemperature();
|
||||
verify(mMockWarnings).showHighTemperatureWarning();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoConfig_noAlarms() {
|
||||
setOverThreshold();
|
||||
final Boolean overheat = false;
|
||||
final Boolean shouldBeepSound = false;
|
||||
TestableResources resources = mContext.getOrCreateTestableResources();
|
||||
resources.addOverride(R.integer.config_showTemperatureWarning, 0);
|
||||
resources.addOverride(R.integer.config_alarmTemperature, 55);
|
||||
resources.addOverride(R.bool.config_alarmTemperatureBeepSound, shouldBeepSound);
|
||||
|
||||
mPowerUI.start();
|
||||
verify(mMockWarnings, never()).notifyHighTemperatureAlarm(overheat, shouldBeepSound);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfig_noAlarms() {
|
||||
setUnderThreshold();
|
||||
final Boolean overheat = false;
|
||||
final Boolean shouldBeepSound = false;
|
||||
TestableResources resources = mContext.getOrCreateTestableResources();
|
||||
resources.addOverride(R.integer.config_showTemperatureAlarm, 1);
|
||||
resources.addOverride(R.integer.config_alarmTemperature, 58);
|
||||
resources.addOverride(R.bool.config_alarmTemperatureBeepSound, shouldBeepSound);
|
||||
|
||||
mPowerUI.start();
|
||||
verify(mMockWarnings, never()).notifyHighTemperatureAlarm(overheat, shouldBeepSound);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfig_alarms() {
|
||||
setOverThreshold();
|
||||
final Boolean overheat = true;
|
||||
final Boolean shouldBeepSound = false;
|
||||
TestableResources resources = mContext.getOrCreateTestableResources();
|
||||
resources.addOverride(R.integer.config_showTemperatureAlarm, 1);
|
||||
resources.addOverride(R.integer.config_alarmTemperature, 58);
|
||||
resources.addOverride(R.bool.config_alarmTemperatureBeepSound, shouldBeepSound);
|
||||
|
||||
mPowerUI.start();
|
||||
verify(mMockWarnings).notifyHighTemperatureAlarm(overheat, shouldBeepSound);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfig_alarmsWithBeepSound() {
|
||||
setOverThreshold();
|
||||
final Boolean overheat = true;
|
||||
final Boolean shouldBeepSound = true;
|
||||
TestableResources resources = mContext.getOrCreateTestableResources();
|
||||
resources.addOverride(R.integer.config_showTemperatureAlarm, 1);
|
||||
resources.addOverride(R.integer.config_alarmTemperature, 58);
|
||||
resources.addOverride(R.bool.config_alarmTemperatureBeepSound, shouldBeepSound);
|
||||
|
||||
mPowerUI.start();
|
||||
verify(mMockWarnings).notifyHighTemperatureAlarm(overheat, shouldBeepSound);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHardPropsThrottlingThreshold_alarms() {
|
||||
setThrottlingThreshold(DEFAULT_OVERHEAT_ALARM_THRESHOLD);
|
||||
setOverThreshold();
|
||||
final Boolean overheat = true;
|
||||
final Boolean shouldBeepSound = false;
|
||||
TestableResources resources = mContext.getOrCreateTestableResources();
|
||||
resources.addOverride(R.integer.config_showTemperatureAlarm, 1);
|
||||
resources.addOverride(R.bool.config_alarmTemperatureBeepSound, shouldBeepSound);
|
||||
|
||||
mPowerUI.start();
|
||||
verify(mMockWarnings).notifyHighTemperatureAlarm(overheat, shouldBeepSound);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHardPropsThrottlingThreshold_noAlarms() {
|
||||
setThrottlingThreshold(DEFAULT_OVERHEAT_ALARM_THRESHOLD);
|
||||
setUnderThreshold();
|
||||
final Boolean overheat = false;
|
||||
final Boolean shouldBeepSound = false;
|
||||
TestableResources resources = mContext.getOrCreateTestableResources();
|
||||
resources.addOverride(R.integer.config_showTemperatureAlarm, 1);
|
||||
resources.addOverride(R.bool.config_alarmTemperatureBeepSound, shouldBeepSound);
|
||||
|
||||
mPowerUI.start();
|
||||
verify(mMockWarnings, never()).notifyHighTemperatureAlarm(overheat, shouldBeepSound);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldShowLowBatteryWarning_showHybridOnly_overrideThresholdHigh_returnsNoShow() {
|
||||
when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
|
||||
@@ -495,7 +582,12 @@ public class PowerUITest extends SysuiTestCase {
|
||||
|
||||
private void setCurrentTemp(float temp) {
|
||||
when(mHardProps.getDeviceTemperatures(DEVICE_TEMPERATURE_SKIN, TEMPERATURE_CURRENT))
|
||||
.thenReturn(new float[] { temp });
|
||||
.thenReturn(new float[] { temp, temp });
|
||||
}
|
||||
|
||||
private void setThrottlingThreshold(float temp) {
|
||||
when(mHardProps.getDeviceTemperatures(DEVICE_TEMPERATURE_SKIN, TEMPERATURE_THROTTLING))
|
||||
.thenReturn(new float[] { temp, temp });
|
||||
}
|
||||
|
||||
private void setOverThreshold() {
|
||||
|
||||
@@ -6117,6 +6117,11 @@ message MetricsEvent {
|
||||
// OS: P
|
||||
FIELD_AUTOFILL_SESSION_ID = 1456;
|
||||
|
||||
// FIELD: Device USB overheat alarm trigger.
|
||||
// CATEGORY: GLOBAL_SYSTEM_UI
|
||||
// OS: P
|
||||
POWER_OVERHEAT_ALARM = 1457;
|
||||
|
||||
// NOTIFICATION_SINCE_INTERRUPTION_MILLIS added to P
|
||||
// NOTIFICATION_INTERRUPTION added to P
|
||||
|
||||
|
||||
Reference in New Issue
Block a user