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