1 /*
2  * Copyright (C) 2016 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.car.systeminterface;
18 
19 import android.car.builtin.power.PowerManagerHelper;
20 import android.car.builtin.util.Slogf;
21 import android.content.Context;
22 import android.hardware.display.DisplayManager;
23 import android.os.PowerManager;
24 import android.os.PowerManager.WakeLock;
25 import android.util.Pair;
26 import android.util.SparseArray;
27 import android.view.Display;
28 
29 import com.android.car.CarLog;
30 import com.android.internal.annotations.GuardedBy;
31 
32 /**
33  * Interface that abstracts wake lock operations
34  */
35 public interface WakeLockInterface {
36 
37     /**
38      * Releases all wakelocks.
39      *
40      * @param displayId Target display
41      */
releaseAllWakeLocks(int displayId)42     void releaseAllWakeLocks(int displayId);
43 
44     /**
45      * Acquires partial wakelock.
46      *
47      * @param displayId Target display
48      * @see android.os.PowerManager#PARTIAL_WAKE_LOCK
49      */
switchToPartialWakeLock(int displayId)50     void switchToPartialWakeLock(int displayId);
51 
52     /**
53      * Acquires full wakelock. This can wake up the display if display is asleep.
54      *
55      * @param displayId Target display
56      */
switchToFullWakeLock(int displayId)57     void switchToFullWakeLock(int displayId);
58 
59     class DefaultImpl implements WakeLockInterface {
60         private static final String TAG = WakeLockInterface.class.getSimpleName();
61 
62         private final Context mContext;
63         private final Object mLock = new Object();
64         @GuardedBy("mLock")
65         private final SparseArray<Pair</* Full */WakeLock, /* Partial */WakeLock>>
66                 mPerDisplayWakeLocks = new SparseArray<>();
67 
DefaultImpl(Context context)68         DefaultImpl(Context context) {
69             mContext = context;
70             DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
71             displayManager.registerDisplayListener(mDisplayListener, /* handler= */ null);
72 
73 
74             for (Display display : displayManager.getDisplays()) {
75                 int displayId = display.getDisplayId();
76                 Pair<WakeLock, WakeLock> wakeLockPair = createWakeLockPair(displayId);
77                 synchronized (mLock) {
78                     mPerDisplayWakeLocks.put(displayId, wakeLockPair);
79                 }
80             }
81         }
82 
83         @Override
switchToPartialWakeLock(int displayId)84         public void switchToPartialWakeLock(int displayId) {
85             Pair<WakeLock, WakeLock> wakeLockPair;
86             synchronized (mLock) {
87                 wakeLockPair = mPerDisplayWakeLocks.get(displayId);
88             }
89             if (wakeLockPair == null) {
90                 Slogf.w(TAG, "WakeLocks for display %d is null", displayId);
91                 return;
92             }
93             WakeLock partialWakeLock = wakeLockPair.second;
94             WakeLock fullWakeLock = wakeLockPair.first;
95             if (!partialWakeLock.isHeld()) {
96                 // CPMS controls the wake lock duration, so we don't use timeout when calling
97                 // acquire().
98                 partialWakeLock.acquire();
99             }
100             if (fullWakeLock.isHeld()) {
101                 fullWakeLock.release();
102             }
103         }
104 
105         @Override
switchToFullWakeLock(int displayId)106         public void switchToFullWakeLock(int displayId) {
107             Pair<WakeLock, WakeLock> wakeLockPair;
108             synchronized (mLock) {
109                 wakeLockPair = mPerDisplayWakeLocks.get(displayId);
110             }
111             if (wakeLockPair == null) {
112                 Slogf.w(TAG, "WakeLocks for display %d is null", displayId);
113                 return;
114             }
115             WakeLock fullWakeLock = wakeLockPair.first;
116             WakeLock partialWakeLock = wakeLockPair.second;
117             if (!fullWakeLock.isHeld()) {
118                 // CPMS controls the wake lock duration, so we don't use timeout when calling
119                 // acquire().
120                 fullWakeLock.acquire();
121             }
122             if (partialWakeLock.isHeld()) {
123                 partialWakeLock.release();
124             }
125         }
126 
127         @Override
releaseAllWakeLocks(int displayId)128         public void releaseAllWakeLocks(int displayId) {
129             Pair<WakeLock, WakeLock> wakeLockPair;
130             synchronized (mLock) {
131                 wakeLockPair = mPerDisplayWakeLocks.get(displayId);
132             }
133             if (wakeLockPair == null) {
134                 Slogf.w(TAG, "WakeLocks for display %d is null", displayId);
135                 return;
136             }
137             WakeLock fullWakeLock = wakeLockPair.first;
138             WakeLock partialWakeLock = wakeLockPair.second;
139             if (fullWakeLock.isHeld()) {
140                 fullWakeLock.release();
141             }
142             if (partialWakeLock.isHeld()) {
143                 partialWakeLock.release();
144             }
145         }
146 
createWakeLockPair(int displayId)147         private Pair<WakeLock, WakeLock> createWakeLockPair(int displayId) {
148             StringBuilder tag = new StringBuilder(CarLog.TAG_POWER).append(":")
149                     .append(displayId);
150             WakeLock fullWakeLock = PowerManagerHelper.newWakeLock(mContext,
151                     PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,
152                     tag.toString(), displayId);
153             WakeLock partialWakeLock = PowerManagerHelper.newWakeLock(mContext,
154                     PowerManager.PARTIAL_WAKE_LOCK, tag.toString(), displayId);
155             Slogf.d(TAG, "createWakeLockPair displayId=%d", displayId);
156             return Pair.create(fullWakeLock, partialWakeLock);
157         }
158 
159         DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() {
160             @Override
161             public void onDisplayAdded(int displayId) {
162                 Slogf.d(TAG, "onDisplayAdded displayId=%d", displayId);
163                 Pair<WakeLock, WakeLock> wakeLockPair = createWakeLockPair(displayId);
164 
165                 synchronized (mLock) {
166                     mPerDisplayWakeLocks.put(displayId, wakeLockPair);
167                 }
168             }
169 
170             @Override
171             public void onDisplayRemoved(int displayId) {
172                 Slogf.d(TAG, "onDisplayRemoved displayId=%d", displayId);
173                 Pair<WakeLock, WakeLock> wakeLockPair;
174                 synchronized (mLock) {
175                     wakeLockPair = mPerDisplayWakeLocks.get(displayId);
176                     if (wakeLockPair == null) {
177                         Slogf.w(TAG, "WakeLocks for display %d is null", displayId);
178                         return;
179                     }
180                     mPerDisplayWakeLocks.remove(displayId);
181                 }
182                 WakeLock fullWakeLock = wakeLockPair.first;
183                 WakeLock partialWakeLock = wakeLockPair.second;
184                 if (fullWakeLock.isHeld()) {
185                     fullWakeLock.release();
186                 }
187                 if (partialWakeLock.isHeld()) {
188                     partialWakeLock.release();
189                 }
190             }
191 
192             @Override
193             public void onDisplayChanged(int displayId) {
194                 // ignore
195             }
196         };
197     }
198 }
199