• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.google.android.car.kitchensink.power;
18 
19 import android.app.ActivityManager;
20 import android.car.hardware.power.CarPowerManager;
21 import android.car.settings.CarSettings;
22 import android.content.Context;
23 import android.hardware.display.DisplayManager;
24 import android.os.Bundle;
25 import android.os.Handler;
26 import android.os.PowerManager;
27 import android.os.Process;
28 import android.os.SystemClock;
29 import android.provider.Settings;
30 import android.util.Log;
31 import android.util.SparseArray;
32 import android.view.Display;
33 import android.view.DisplayAddress;
34 import android.view.LayoutInflater;
35 import android.view.View;
36 import android.view.ViewGroup;
37 import android.widget.ArrayAdapter;
38 import android.widget.Button;
39 import android.widget.RadioButton;
40 import android.widget.RadioGroup;
41 import android.widget.RadioGroup.OnCheckedChangeListener;
42 import android.widget.Spinner;
43 import android.widget.TextView;
44 
45 import androidx.fragment.app.Fragment;
46 
47 import com.google.android.car.kitchensink.KitchenSinkActivity;
48 import com.google.android.car.kitchensink.R;
49 
50 import java.util.ArrayList;
51 
52 public class PowerTestFragment extends Fragment {
53     private static final boolean DBG = false;
54     private static final String TAG = "PowerTestFragment";
55 
56     private CarPowerManager mCarPowerManager;
57     private DisplayManager mDisplayManager;
58     private Spinner mDisplaySpinner;
59     private ViewGroup mPowerModeViewGroup;
60     private SparseArray<RadioGroup> mRadioGroupList;
61 
62     private static final int MODE_OFF = 0;
63     private static final int MODE_ON = 1;
64     private static final int MODE_ALWAYS_ON = 2;
65 
66     private final CarPowerManager.CarPowerStateListener mPowerListener =
67             (state) -> {
68                 Log.i(TAG, "onStateChanged() state = " + state);
69             };
70 
71     @Override
onCreate(Bundle savedInstanceState)72     public void onCreate(Bundle savedInstanceState) {
73         final Runnable r = () -> {
74             mCarPowerManager = ((KitchenSinkActivity) getActivity()).getPowerManager();
75             try {
76                 mCarPowerManager.setListener(getContext().getMainExecutor(), mPowerListener);
77             } catch (IllegalStateException e) {
78                 Log.e(TAG, "CarPowerManager listener was not cleared");
79             }
80         };
81         ((KitchenSinkActivity) getActivity()).requestRefreshManager(r,
82                 new Handler(getContext().getMainLooper()));
83         super.onCreate(savedInstanceState);
84         mDisplayManager = getContext().getSystemService(DisplayManager.class);
85         mRadioGroupList = new SparseArray<>();
86     }
87 
88     @Override
onDestroy()89     public void onDestroy() {
90         super.onDestroy();
91         mCarPowerManager.clearListener();
92     }
93 
94     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance)95     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) {
96         View v = inflater.inflate(R.layout.power_test, container, false);
97 
98         Button b = v.findViewById(R.id.btnPwrRequestShutdown);
99         b.setOnClickListener(this::requestShutdownBtn);
100 
101         b = v.findViewById(R.id.btnPwrShutdown);
102         b.setOnClickListener(this::shutdownBtn);
103 
104         b = v.findViewById(R.id.btnPwrSleep);
105         b.setOnClickListener(this::sleepBtn);
106 
107         b = v.findViewById(R.id.btnDisplayOn);
108         b.setOnClickListener(this::displayOnBtn);
109 
110         b = v.findViewById(R.id.btnDisplayOff);
111         b.setOnClickListener(this::displayOffBtn);
112 
113         mDisplaySpinner = v.findViewById(R.id.display_spinner);
114         mPowerModeViewGroup = v.findViewById(R.id.power_mode_layout);
115 
116         if(DBG) {
117             Log.d(TAG, "Starting PowerTestFragment");
118         }
119 
120         return v;
121     }
122 
123     @Override
onViewCreated(View view, Bundle savedInstanceState)124     public void onViewCreated(View view, Bundle savedInstanceState) {
125         super.onViewCreated(view, savedInstanceState);
126         mDisplaySpinner.setAdapter(new ArrayAdapter<>(getContext(),
127                 android.R.layout.simple_spinner_item, getDisplays()));
128 
129         // Display power mode for each passenger display is set to {@code PowerTestFragment.MODE_ON}
130         // whenever this fragment is created.
131         updateRadioGroups();
132         updateDisplayModeSetting();
133     }
134 
requestShutdownBtn(View v)135     private void requestShutdownBtn(View v) {
136         mCarPowerManager.requestShutdownOnNextSuspend();
137     }
138 
shutdownBtn(View v)139     private void shutdownBtn(View v) {
140         if (ActivityManager.isUserAMonkey()) {
141             return;
142         }
143         if(DBG) {
144             Log.d(TAG, "Calling shutdown method");
145         }
146         PowerManager pm = (PowerManager) getActivity().getSystemService(Context.POWER_SERVICE);
147         pm.shutdown(/* confirm */ false, /* reason */ null, /* wait */ false);
148         Log.d(TAG, "shutdown called!");
149     }
150 
sleepBtn(View v)151     private void sleepBtn(View v) {
152         if(DBG) {
153             Log.d(TAG, "Calling sleep method");
154         }
155         // NOTE:  This doesn't really work to sleep the device.  Actual sleep is implemented via
156         //  SystemInterface via libsuspend::force_suspend()
157         PowerManager pm = (PowerManager) getActivity().getSystemService(Context.POWER_SERVICE);
158         pm.goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN,
159                      PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
160     }
161 
displayOnBtn(View v)162     private void displayOnBtn(View v) {
163         if (DBG) {
164             Log.d(TAG, "Calling display on method");
165         }
166         int selectedDisplayId = (Integer) mDisplaySpinner.getSelectedItem();
167         mCarPowerManager.setDisplayPowerState(selectedDisplayId, /* enable */ true);
168     }
169 
displayOffBtn(View v)170     private void displayOffBtn(View v) {
171         if (DBG) {
172             Log.d(TAG, "Calling display off method");
173         }
174         int selectedDisplayId = (Integer) mDisplaySpinner.getSelectedItem();
175         mCarPowerManager.setDisplayPowerState(selectedDisplayId, /* enable */ false);
176     }
177 
updateRadioGroups()178     private void updateRadioGroups() {
179         mPowerModeViewGroup.removeAllViews();
180         for (Display display : mDisplayManager.getDisplays()) {
181             int displayId = display.getDisplayId();
182             if (!getDisplays().contains(displayId)) {
183                 continue;
184             }
185             RadioButton butnOff = new RadioButton(getContext());
186             butnOff.setText("OFF");
187             RadioButton btnOn = new RadioButton(getContext());
188             btnOn.setText("ON");
189             RadioButton btnAlwaysOn = new RadioButton(getContext());
190             btnAlwaysOn.setText("ALWAYS ON");
191 
192             RadioGroup group = new RadioGroup(getContext());
193             group.addView(butnOff, MODE_OFF);
194             group.addView(btnOn, MODE_ON);
195             group.addView(btnAlwaysOn, MODE_ALWAYS_ON);
196             group.check(btnOn.getId());
197             group.setOnCheckedChangeListener(mListener);
198 
199             TextView tv = new TextView(getContext());
200             tv.setText("Display: " + displayId);
201             mPowerModeViewGroup.addView(tv);
202             mPowerModeViewGroup.addView(group);
203             mRadioGroupList.put(displayId, group);
204         }
205     }
206 
updateDisplayModeSetting()207     private void updateDisplayModeSetting() {
208         StringBuilder sb = new StringBuilder();
209         int displayPort = getDisplayPort(Display.DEFAULT_DISPLAY);
210         sb.append(displayPort).append(":").append(MODE_ALWAYS_ON);
211         for (int i = 0; i < mRadioGroupList.size(); i++) {
212             if (sb.length() != 0) {
213                 sb.append(",");
214             }
215             int displayId = mRadioGroupList.keyAt(i);
216             RadioGroup group = mRadioGroupList.get(displayId);
217             RadioButton btnMode = group.findViewById(group.getCheckedRadioButtonId());
218             int mode = textToValue(btnMode.getText().toString());
219 
220             displayPort = getDisplayPort(displayId);
221             sb.append(displayPort).append(":").append(mode);
222         }
223         String value = sb.toString();
224         if (DBG) {
225             Log.d(TAG, "Setting value to " + value);
226         }
227         Settings.Global.putString(getContext().getContentResolver(),
228                 CarSettings.Global.DISPLAY_POWER_MODE, value);
229     }
230 
getDisplayPort(int displayId)231     private int getDisplayPort(int displayId) {
232         Display display = mDisplayManager.getDisplay(displayId);
233         if (display != null) {
234             DisplayAddress address = display.getAddress();
235             if (address instanceof DisplayAddress.Physical) {
236                 DisplayAddress.Physical physicalAddress = (DisplayAddress.Physical) address;
237                 return physicalAddress.getPort();
238             }
239         }
240         return Display.INVALID_DISPLAY;
241     }
242 
243     private OnCheckedChangeListener mListener = new OnCheckedChangeListener() {
244         @Override
245         public void onCheckedChanged(RadioGroup group, int checkedId) {
246             updateDisplayModeSetting();
247         }
248     };
249 
250     DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() {
251         @Override
252         public void onDisplayAdded(int displayId) {
253             updateRadioGroups();
254         }
255 
256         @Override
257         public void onDisplayRemoved(int displayId) {
258             updateRadioGroups();
259         }
260 
261         @Override
262         public void onDisplayChanged(int displayId) {
263             // do nothing
264         }
265     };
266 
textToValue(String mode)267     private static int textToValue(String mode) {
268         switch (mode) {
269             case "OFF":
270                 return MODE_OFF;
271             case "ON":
272                 return MODE_ON;
273             case "ALWAYS ON":
274             default:
275                 return MODE_ALWAYS_ON;
276         }
277     }
278 
getDisplays()279     private ArrayList<Integer> getDisplays() {
280         ArrayList<Integer> displayIds = new ArrayList<>();
281         Display[] displays = mDisplayManager.getDisplays();
282         int uidSelf = Process.myUid();
283         for (Display disp : displays) {
284             if (!disp.hasAccess(uidSelf)) {
285                 continue;
286             }
287             if (disp.getDisplayId() == Display.DEFAULT_DISPLAY) {
288                 continue;
289             }
290             displayIds.add(disp.getDisplayId());
291         }
292         return displayIds;
293     }
294 }
295