blob: db15909196c7dfdc9c9ed575ebe8208c1f9611aa [file] [log] [blame]
Bill Lin32ed3d62018-10-02 18:10:09 +08001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.power;
18
19import static android.content.Context.VIBRATOR_SERVICE;
20
21import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
22
23import android.content.ContentResolver;
24import android.content.Context;
25import android.media.AudioAttributes;
26import android.net.Uri;
27import android.os.Handler;
28import android.os.VibrationEffect;
29import android.os.Vibrator;
30
31import com.android.internal.annotations.VisibleForTesting;
32import com.android.systemui.R;
33import com.android.systemui.media.NotificationPlayer;
34
35/**
36 * A Controller handle beep sound, vibration and TTS depend on state of OverheatAlarmDialog.
37 */
38public class OverheatAlarmController {
39 private static final String TAG = OverheatAlarmController.class.getSimpleName();
40
41 private static final int VIBRATION_INTERVAL = 2000;
42 private static final long[] VIBRATION_PATTERN = new long[]{0, 400, 200, 400, 200, 400, 200};
43
44 private static OverheatAlarmController sInstance;
45
46 private final Vibrator mVibrator;
47
48 private NotificationPlayer mPlayer;
49 private VibrationEffect mVibrationEffect;
50
51 private boolean mShouldVibrate;
52
53 /**
54 * The constructor only used to create singleton sInstance.
55 */
56 private OverheatAlarmController(Context context) {
57 mVibrator = (Vibrator) context.getSystemService(VIBRATOR_SERVICE);
58 }
59
60 /**
61 * Get singleton OverheatAlarmController instance.
62 */
63 public static OverheatAlarmController getInstance(Context context) {
64 if (context == null) {
65 throw new IllegalArgumentException();
66 }
67 if (sInstance == null) {
68 sInstance = new OverheatAlarmController(context);
69 }
70 return sInstance;
71 }
72
73 /**
74 * Starting alarm beep sound and vibration.
75 */
76 @VisibleForTesting
77 public void startAlarm(Context context) {
78 if (mPlayer != null) {
79 return;
80 }
81 playSound(context);
82 startVibrate();
83 }
84
85 /**
86 * Stop alarming beep sound, vibrating, and TTS if initialized.
87 */
88 @VisibleForTesting
89 public void stopAlarm() {
90 stopPlayer();
91 stopVibrate();
92 }
93
94 @VisibleForTesting
95 protected void playSound(Context context) {
96 Uri uri = new Uri.Builder()
97 .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
98 .authority(context.getBasePackageName())
99 .appendPath(Integer.toString(R.raw.overheat_alarm)).build();
100
101 if (mPlayer == null) {
102 mPlayer = new NotificationPlayer(TAG);
103 }
104 mPlayer.setUsesWakeLock(context);
105 mPlayer.play(context, uri, true /* looping */, getAlertAudioAttributes());
106 }
107
108 @VisibleForTesting
109 protected void stopPlayer() {
110 if (mPlayer != null) {
111 mPlayer.stop();
112 mPlayer = null;
113 }
114 }
115
116 @VisibleForTesting
117 protected void startVibrate() {
118 mShouldVibrate = true;
119 if (mVibrationEffect == null) {
120 mVibrationEffect = VibrationEffect.createWaveform(VIBRATION_PATTERN, -1);
121 }
122 performVibrate();
123 }
124
125 @VisibleForTesting
126 protected void performVibrate() {
127 if (mShouldVibrate && mVibrator != null) {
128 mVibrator.vibrate(mVibrationEffect, getAlertAudioAttributes());
129 Handler.getMain().sendMessageDelayed(
130 obtainMessage(OverheatAlarmController::performVibrate, this),
131 VIBRATION_INTERVAL);
132 }
133 }
134
135 @VisibleForTesting
136 protected void stopVibrate() {
137 if (mVibrator != null) {
138 mVibrator.cancel();
139 }
140 mShouldVibrate = false;
141 }
142
143 /**
144 * Build AudioAttributes for mPlayer(NotificationPlayer) and vibrator
145 * Use the alarm channel so it can vibrate in DnD mode, unless alarms are
146 * specifically disabled in DnD.
147 */
148 private static AudioAttributes getAlertAudioAttributes() {
149 AudioAttributes.Builder builder = new AudioAttributes.Builder();
150 builder.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION);
151 builder.setUsage(AudioAttributes.USAGE_ALARM);
152 // Set FLAG_BYPASS_INTERRUPTION_POLICY and FLAG_BYPASS_MUTE so that it enables
153 // audio in any DnD mode, even in total silence DnD mode (requires MODIFY_PHONE_STATE).
154 builder.setFlags(
155 AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY | AudioAttributes.FLAG_BYPASS_MUTE);
156 return builder.build();
157 }
158}