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