1 /*
2  * Copyright (C) 2014 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 
17 package com.android.systemui.statusbar.phone;
18 
19 import android.content.Context;
20 import android.os.SystemProperties;
21 import android.os.UserHandle;
22 import android.provider.Settings;
23 import android.text.TextUtils;
24 import android.util.MathUtils;
25 import android.util.SparseBooleanArray;
26 
27 import com.android.internal.hardware.AmbientDisplayConfiguration;
28 import com.android.systemui.R;
29 
30 import java.io.PrintWriter;
31 
32 public class DozeParameters {
33     private static final int MAX_DURATION = 60 * 1000;
34     public static final String DOZE_SENSORS_WAKE_UP_FULLY = "doze_sensors_wake_up_fully";
35 
36     private final Context mContext;
37     private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
38 
39     private static IntInOutMatcher sPickupSubtypePerformsProxMatcher;
40 
DozeParameters(Context context)41     public DozeParameters(Context context) {
42         mContext = context;
43         mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
44     }
45 
dump(PrintWriter pw)46     public void dump(PrintWriter pw) {
47         pw.println("  DozeParameters:");
48         pw.print("    getDisplayStateSupported(): "); pw.println(getDisplayStateSupported());
49         pw.print("    getPulseDuration(pickup=false): "); pw.println(getPulseDuration(false));
50         pw.print("    getPulseDuration(pickup=true): "); pw.println(getPulseDuration(true));
51         pw.print("    getPulseInDuration(pickup=false): "); pw.println(getPulseInDuration(false));
52         pw.print("    getPulseInDuration(pickup=true): "); pw.println(getPulseInDuration(true));
53         pw.print("    getPulseInVisibleDuration(): "); pw.println(getPulseVisibleDuration());
54         pw.print("    getPulseOutDuration(): "); pw.println(getPulseOutDuration());
55         pw.print("    getPulseOnSigMotion(): "); pw.println(getPulseOnSigMotion());
56         pw.print("    getVibrateOnSigMotion(): "); pw.println(getVibrateOnSigMotion());
57         pw.print("    getVibrateOnPickup(): "); pw.println(getVibrateOnPickup());
58         pw.print("    getProxCheckBeforePulse(): "); pw.println(getProxCheckBeforePulse());
59         pw.print("    getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold());
60         pw.print("    getPickupSubtypePerformsProxCheck(): ");pw.println(
61                 dumpPickupSubtypePerformsProxCheck());
62     }
63 
dumpPickupSubtypePerformsProxCheck()64     private String dumpPickupSubtypePerformsProxCheck() {
65         // Refresh sPickupSubtypePerformsProxMatcher
66         getPickupSubtypePerformsProxCheck(0);
67 
68         if (sPickupSubtypePerformsProxMatcher == null) {
69             return "fallback: " + mContext.getResources().getBoolean(
70                     R.bool.doze_pickup_performs_proximity_check);
71         } else {
72             return "spec: " + sPickupSubtypePerformsProxMatcher.mSpec;
73         }
74     }
75 
getDisplayStateSupported()76     public boolean getDisplayStateSupported() {
77         return getBoolean("doze.display.supported", R.bool.doze_display_state_supported);
78     }
79 
getPulseDuration(boolean pickup)80     public int getPulseDuration(boolean pickup) {
81         return getPulseInDuration(pickup) + getPulseVisibleDuration() + getPulseOutDuration();
82     }
83 
getPulseInDuration(boolean pickupOrDoubleTap)84     public int getPulseInDuration(boolean pickupOrDoubleTap) {
85         return pickupOrDoubleTap
86                 ? getInt("doze.pulse.duration.in.pickup", R.integer.doze_pulse_duration_in_pickup)
87                 : getInt("doze.pulse.duration.in", R.integer.doze_pulse_duration_in);
88     }
89 
getPulseVisibleDuration()90     public int getPulseVisibleDuration() {
91         return getInt("doze.pulse.duration.visible", R.integer.doze_pulse_duration_visible);
92     }
93 
getPulseOutDuration()94     public int getPulseOutDuration() {
95         return getInt("doze.pulse.duration.out", R.integer.doze_pulse_duration_out);
96     }
97 
getPulseOnSigMotion()98     public boolean getPulseOnSigMotion() {
99         return getBoolean("doze.pulse.sigmotion", R.bool.doze_pulse_on_significant_motion);
100     }
101 
getVibrateOnSigMotion()102     public boolean getVibrateOnSigMotion() {
103         return SystemProperties.getBoolean("doze.vibrate.sigmotion", false);
104     }
105 
getVibrateOnPickup()106     public boolean getVibrateOnPickup() {
107         return SystemProperties.getBoolean("doze.vibrate.pickup", false);
108     }
109 
getProxCheckBeforePulse()110     public boolean getProxCheckBeforePulse() {
111         return getBoolean("doze.pulse.proxcheck", R.bool.doze_proximity_check_before_pulse);
112     }
113 
getPickupVibrationThreshold()114     public int getPickupVibrationThreshold() {
115         return getInt("doze.pickup.vibration.threshold", R.integer.doze_pickup_vibration_threshold);
116     }
117 
getAlwaysOn()118     public boolean getAlwaysOn() {
119         return mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT);
120     }
121 
getBoolean(String propName, int resId)122     private boolean getBoolean(String propName, int resId) {
123         return SystemProperties.getBoolean(propName, mContext.getResources().getBoolean(resId));
124     }
125 
getInt(String propName, int resId)126     private int getInt(String propName, int resId) {
127         int value = SystemProperties.getInt(propName, mContext.getResources().getInteger(resId));
128         return MathUtils.constrain(value, 0, MAX_DURATION);
129     }
130 
getString(String propName, int resId)131     private String getString(String propName, int resId) {
132         return SystemProperties.get(propName, mContext.getString(resId));
133     }
134 
getPickupSubtypePerformsProxCheck(int subType)135     public boolean getPickupSubtypePerformsProxCheck(int subType) {
136         String spec = getString("doze.pickup.proxcheck",
137                 R.string.doze_pickup_subtype_performs_proximity_check);
138 
139         if (TextUtils.isEmpty(spec)) {
140             // Fall back to non-subtype based property.
141             return mContext.getResources().getBoolean(R.bool.doze_pickup_performs_proximity_check);
142         }
143 
144         if (sPickupSubtypePerformsProxMatcher == null
145                 || !TextUtils.equals(spec, sPickupSubtypePerformsProxMatcher.mSpec)) {
146             sPickupSubtypePerformsProxMatcher = new IntInOutMatcher(spec);
147         }
148 
149         return sPickupSubtypePerformsProxMatcher.isIn(subType);
150     }
151 
getPulseVisibleDurationExtended()152     public int getPulseVisibleDurationExtended() {
153         return 2 * getPulseVisibleDuration();
154     }
155 
156 
157     /**
158      * Parses a spec of the form `1,2,3,!5,*`. The resulting object will match numbers that are
159      * listed, will not match numbers that are listed with a ! prefix, and will match / not match
160      * unlisted numbers depending on whether * or !* is present.
161      *
162      * *  -> match any numbers that are not explicitly listed
163      * !* -> don't match any numbers that are not explicitly listed
164      * 2  -> match 2
165      * !3 -> don't match 3
166      *
167      * It is illegal to specify:
168      * - an empty spec
169      * - a spec containing that are empty, or a lone !
170      * - a spec for anything other than numbers or *
171      * - multiple terms for the same number / multiple *s
172      */
173     public static class IntInOutMatcher {
174         private static final String WILDCARD = "*";
175         private static final char OUT_PREFIX = '!';
176 
177         private final SparseBooleanArray mIsIn;
178         private final boolean mDefaultIsIn;
179         final String mSpec;
180 
IntInOutMatcher(String spec)181         public IntInOutMatcher(String spec) {
182             if (TextUtils.isEmpty(spec)) {
183                 throw new IllegalArgumentException("Spec must not be empty");
184             }
185 
186             boolean defaultIsIn = false;
187             boolean foundWildcard = false;
188 
189             mSpec = spec;
190             mIsIn = new SparseBooleanArray();
191 
192             for (String itemPrefixed : spec.split(",", -1)) {
193                 if (itemPrefixed.length() == 0) {
194                     throw new IllegalArgumentException(
195                             "Illegal spec, must not have zero-length items: `" + spec + "`");
196                 }
197                 boolean isIn = itemPrefixed.charAt(0) != OUT_PREFIX;
198                 String item = isIn ? itemPrefixed : itemPrefixed.substring(1);
199 
200                 if (itemPrefixed.length() == 0) {
201                     throw new IllegalArgumentException(
202                             "Illegal spec, must not have zero-length items: `" + spec + "`");
203                 }
204 
205                 if (WILDCARD.equals(item)) {
206                     if (foundWildcard) {
207                         throw new IllegalArgumentException("Illegal spec, `" + WILDCARD +
208                                 "` must not appear multiple times in `" + spec + "`");
209                     }
210                     defaultIsIn = isIn;
211                     foundWildcard = true;
212                 } else {
213                     int key = Integer.parseInt(item);
214                     if (mIsIn.indexOfKey(key) >= 0) {
215                         throw new IllegalArgumentException("Illegal spec, `" + key +
216                                 "` must not appear multiple times in `" + spec + "`");
217                     }
218                     mIsIn.put(key, isIn);
219                 }
220             }
221 
222             if (!foundWildcard) {
223                 throw new IllegalArgumentException("Illegal spec, must specify either * or !*");
224             }
225 
226             mDefaultIsIn = defaultIsIn;
227         }
228 
isIn(int value)229         public boolean isIn(int value) {
230             return (mIsIn.get(value, mDefaultIsIn));
231         }
232     }
233 }
234