1 /* 2 * Copyright (C) 2012 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.server.display; 18 19 import android.annotation.Nullable; 20 import android.graphics.Point; 21 import android.hardware.display.BrightnessConfiguration; 22 import android.hardware.display.WifiDisplay; 23 import android.util.AtomicFile; 24 import android.util.Slog; 25 import android.util.SparseArray; 26 import android.util.SparseLongArray; 27 import android.util.TimeUtils; 28 import android.util.Xml; 29 import android.view.Display; 30 31 import com.android.internal.annotations.VisibleForTesting; 32 import com.android.internal.util.FastXmlSerializer; 33 import com.android.internal.util.XmlUtils; 34 35 import libcore.io.IoUtils; 36 37 import org.xmlpull.v1.XmlPullParser; 38 import org.xmlpull.v1.XmlPullParserException; 39 import org.xmlpull.v1.XmlSerializer; 40 41 import java.io.BufferedInputStream; 42 import java.io.BufferedOutputStream; 43 import java.io.File; 44 import java.io.FileNotFoundException; 45 import java.io.FileOutputStream; 46 import java.io.IOException; 47 import java.io.InputStream; 48 import java.io.OutputStream; 49 import java.io.PrintWriter; 50 import java.nio.charset.StandardCharsets; 51 import java.util.ArrayList; 52 import java.util.HashMap; 53 import java.util.Map; 54 import java.util.Objects; 55 56 /** 57 * Manages persistent state recorded by the display manager service as an XML file. 58 * Caller must acquire lock on the data store before accessing it. 59 * 60 * File format: 61 * <code> 62 * <display-manager-state> 63 * <remembered-wifi-displays> 64 * <wifi-display deviceAddress="00:00:00:00:00:00" deviceName="XXXX" deviceAlias="YYYY" /> 65 * <remembered-wifi-displays> 66 * <display-states> 67 * <display unique-id="XXXXXXX"> 68 * <color-mode>0</color-mode> 69 * </display> 70 * </display-states> 71 * <stable-device-values> 72 * <stable-display-height>1920</stable-display-height> 73 * <stable-display-width>1080</stable-display-width> 74 * </stable-device-values> 75 * <brightness-configurations> 76 * <brightness-configuration user-serial="0" package-name="com.example" timestamp="1234"> 77 * <brightness-curve description="some text"> 78 * <brightness-point lux="0" nits="13.25"/> 79 * <brightness-point lux="20" nits="35.94"/> 80 * </brightness-curve> 81 * </brightness-configuration> 82 * </brightness-configurations> 83 * </display-manager-state> 84 * </code> 85 * 86 * TODO: refactor this to extract common code shared with the input manager's data store 87 */ 88 final class PersistentDataStore { 89 static final String TAG = "DisplayManager"; 90 91 private static final String TAG_DISPLAY_MANAGER_STATE = "display-manager-state"; 92 93 private static final String TAG_REMEMBERED_WIFI_DISPLAYS = "remembered-wifi-displays"; 94 private static final String TAG_WIFI_DISPLAY = "wifi-display"; 95 private static final String ATTR_DEVICE_ADDRESS = "deviceAddress"; 96 private static final String ATTR_DEVICE_NAME = "deviceName"; 97 private static final String ATTR_DEVICE_ALIAS = "deviceAlias"; 98 99 private static final String TAG_DISPLAY_STATES = "display-states"; 100 private static final String TAG_DISPLAY = "display"; 101 private static final String TAG_COLOR_MODE = "color-mode"; 102 private static final String ATTR_UNIQUE_ID = "unique-id"; 103 104 private static final String TAG_STABLE_DEVICE_VALUES = "stable-device-values"; 105 private static final String TAG_STABLE_DISPLAY_HEIGHT = "stable-display-height"; 106 private static final String TAG_STABLE_DISPLAY_WIDTH = "stable-display-width"; 107 108 private static final String TAG_BRIGHTNESS_CONFIGURATIONS = "brightness-configurations"; 109 private static final String TAG_BRIGHTNESS_CONFIGURATION = "brightness-configuration"; 110 private static final String ATTR_USER_SERIAL = "user-serial"; 111 private static final String ATTR_PACKAGE_NAME = "package-name"; 112 private static final String ATTR_TIME_STAMP = "timestamp"; 113 114 // Remembered Wifi display devices. 115 private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>(); 116 117 // Display state by unique id. 118 private final HashMap<String, DisplayState> mDisplayStates = 119 new HashMap<String, DisplayState>(); 120 121 // Display values which should be stable across the device's lifetime. 122 private final StableDeviceValues mStableDeviceValues = new StableDeviceValues(); 123 124 // Brightness configuration by user 125 private BrightnessConfigurations mBrightnessConfigurations = new BrightnessConfigurations(); 126 127 // True if the data has been loaded. 128 private boolean mLoaded; 129 130 // True if there are changes to be saved. 131 private boolean mDirty; 132 133 // The interface for methods which should be replaced by the test harness. 134 private Injector mInjector; 135 PersistentDataStore()136 public PersistentDataStore() { 137 this(new Injector()); 138 } 139 140 @VisibleForTesting PersistentDataStore(Injector injector)141 PersistentDataStore(Injector injector) { 142 mInjector = injector; 143 } 144 saveIfNeeded()145 public void saveIfNeeded() { 146 if (mDirty) { 147 save(); 148 mDirty = false; 149 } 150 } 151 getRememberedWifiDisplay(String deviceAddress)152 public WifiDisplay getRememberedWifiDisplay(String deviceAddress) { 153 loadIfNeeded(); 154 int index = findRememberedWifiDisplay(deviceAddress); 155 if (index >= 0) { 156 return mRememberedWifiDisplays.get(index); 157 } 158 return null; 159 } 160 getRememberedWifiDisplays()161 public WifiDisplay[] getRememberedWifiDisplays() { 162 loadIfNeeded(); 163 return mRememberedWifiDisplays.toArray(new WifiDisplay[mRememberedWifiDisplays.size()]); 164 } 165 applyWifiDisplayAlias(WifiDisplay display)166 public WifiDisplay applyWifiDisplayAlias(WifiDisplay display) { 167 if (display != null) { 168 loadIfNeeded(); 169 170 String alias = null; 171 int index = findRememberedWifiDisplay(display.getDeviceAddress()); 172 if (index >= 0) { 173 alias = mRememberedWifiDisplays.get(index).getDeviceAlias(); 174 } 175 if (!Objects.equals(display.getDeviceAlias(), alias)) { 176 return new WifiDisplay(display.getDeviceAddress(), display.getDeviceName(), 177 alias, display.isAvailable(), display.canConnect(), display.isRemembered()); 178 } 179 } 180 return display; 181 } 182 applyWifiDisplayAliases(WifiDisplay[] displays)183 public WifiDisplay[] applyWifiDisplayAliases(WifiDisplay[] displays) { 184 WifiDisplay[] results = displays; 185 if (results != null) { 186 int count = displays.length; 187 for (int i = 0; i < count; i++) { 188 WifiDisplay result = applyWifiDisplayAlias(displays[i]); 189 if (result != displays[i]) { 190 if (results == displays) { 191 results = new WifiDisplay[count]; 192 System.arraycopy(displays, 0, results, 0, count); 193 } 194 results[i] = result; 195 } 196 } 197 } 198 return results; 199 } 200 rememberWifiDisplay(WifiDisplay display)201 public boolean rememberWifiDisplay(WifiDisplay display) { 202 loadIfNeeded(); 203 204 int index = findRememberedWifiDisplay(display.getDeviceAddress()); 205 if (index >= 0) { 206 WifiDisplay other = mRememberedWifiDisplays.get(index); 207 if (other.equals(display)) { 208 return false; // already remembered without change 209 } 210 mRememberedWifiDisplays.set(index, display); 211 } else { 212 mRememberedWifiDisplays.add(display); 213 } 214 setDirty(); 215 return true; 216 } 217 forgetWifiDisplay(String deviceAddress)218 public boolean forgetWifiDisplay(String deviceAddress) { 219 loadIfNeeded(); 220 int index = findRememberedWifiDisplay(deviceAddress); 221 if (index >= 0) { 222 mRememberedWifiDisplays.remove(index); 223 setDirty(); 224 return true; 225 } 226 return false; 227 } 228 findRememberedWifiDisplay(String deviceAddress)229 private int findRememberedWifiDisplay(String deviceAddress) { 230 int count = mRememberedWifiDisplays.size(); 231 for (int i = 0; i < count; i++) { 232 if (mRememberedWifiDisplays.get(i).getDeviceAddress().equals(deviceAddress)) { 233 return i; 234 } 235 } 236 return -1; 237 } 238 getColorMode(DisplayDevice device)239 public int getColorMode(DisplayDevice device) { 240 if (!device.hasStableUniqueId()) { 241 return Display.COLOR_MODE_INVALID; 242 } 243 DisplayState state = getDisplayState(device.getUniqueId(), false); 244 if (state == null) { 245 return Display.COLOR_MODE_INVALID; 246 } 247 return state.getColorMode(); 248 } 249 setColorMode(DisplayDevice device, int colorMode)250 public boolean setColorMode(DisplayDevice device, int colorMode) { 251 if (!device.hasStableUniqueId()) { 252 return false; 253 } 254 DisplayState state = getDisplayState(device.getUniqueId(), true); 255 if (state.setColorMode(colorMode)) { 256 setDirty(); 257 return true; 258 } 259 return false; 260 } 261 getStableDisplaySize()262 public Point getStableDisplaySize() { 263 loadIfNeeded(); 264 return mStableDeviceValues.getDisplaySize(); 265 } 266 setStableDisplaySize(Point size)267 public void setStableDisplaySize(Point size) { 268 loadIfNeeded(); 269 if (mStableDeviceValues.setDisplaySize(size)) { 270 setDirty(); 271 } 272 } 273 setBrightnessConfigurationForUser(BrightnessConfiguration c, int userSerial, @Nullable String packageName)274 public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userSerial, 275 @Nullable String packageName) { 276 loadIfNeeded(); 277 if (mBrightnessConfigurations.setBrightnessConfigurationForUser(c, userSerial, 278 packageName)) { 279 setDirty(); 280 } 281 } 282 getBrightnessConfiguration(int userSerial)283 public BrightnessConfiguration getBrightnessConfiguration(int userSerial) { 284 loadIfNeeded(); 285 return mBrightnessConfigurations.getBrightnessConfiguration(userSerial); 286 } 287 getDisplayState(String uniqueId, boolean createIfAbsent)288 private DisplayState getDisplayState(String uniqueId, boolean createIfAbsent) { 289 loadIfNeeded(); 290 DisplayState state = mDisplayStates.get(uniqueId); 291 if (state == null && createIfAbsent) { 292 state = new DisplayState(); 293 mDisplayStates.put(uniqueId, state); 294 setDirty(); 295 } 296 return state; 297 } 298 loadIfNeeded()299 public void loadIfNeeded() { 300 if (!mLoaded) { 301 load(); 302 mLoaded = true; 303 } 304 } 305 setDirty()306 private void setDirty() { 307 mDirty = true; 308 } 309 clearState()310 private void clearState() { 311 mRememberedWifiDisplays.clear(); 312 } 313 load()314 private void load() { 315 clearState(); 316 317 final InputStream is; 318 try { 319 is = mInjector.openRead(); 320 } catch (FileNotFoundException ex) { 321 return; 322 } 323 324 XmlPullParser parser; 325 try { 326 parser = Xml.newPullParser(); 327 parser.setInput(new BufferedInputStream(is), StandardCharsets.UTF_8.name()); 328 loadFromXml(parser); 329 } catch (IOException ex) { 330 Slog.w(TAG, "Failed to load display manager persistent store data.", ex); 331 clearState(); 332 } catch (XmlPullParserException ex) { 333 Slog.w(TAG, "Failed to load display manager persistent store data.", ex); 334 clearState(); 335 } finally { 336 IoUtils.closeQuietly(is); 337 } 338 } 339 save()340 private void save() { 341 final OutputStream os; 342 try { 343 os = mInjector.startWrite(); 344 boolean success = false; 345 try { 346 XmlSerializer serializer = new FastXmlSerializer(); 347 serializer.setOutput(new BufferedOutputStream(os), StandardCharsets.UTF_8.name()); 348 saveToXml(serializer); 349 serializer.flush(); 350 success = true; 351 } finally { 352 mInjector.finishWrite(os, success); 353 } 354 } catch (IOException ex) { 355 Slog.w(TAG, "Failed to save display manager persistent store data.", ex); 356 } 357 } 358 loadFromXml(XmlPullParser parser)359 private void loadFromXml(XmlPullParser parser) 360 throws IOException, XmlPullParserException { 361 XmlUtils.beginDocument(parser, TAG_DISPLAY_MANAGER_STATE); 362 final int outerDepth = parser.getDepth(); 363 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 364 if (parser.getName().equals(TAG_REMEMBERED_WIFI_DISPLAYS)) { 365 loadRememberedWifiDisplaysFromXml(parser); 366 } 367 if (parser.getName().equals(TAG_DISPLAY_STATES)) { 368 loadDisplaysFromXml(parser); 369 } 370 if (parser.getName().equals(TAG_STABLE_DEVICE_VALUES)) { 371 mStableDeviceValues.loadFromXml(parser); 372 } 373 if (parser.getName().equals(TAG_BRIGHTNESS_CONFIGURATIONS)) { 374 mBrightnessConfigurations.loadFromXml(parser); 375 } 376 } 377 } 378 loadRememberedWifiDisplaysFromXml(XmlPullParser parser)379 private void loadRememberedWifiDisplaysFromXml(XmlPullParser parser) 380 throws IOException, XmlPullParserException { 381 final int outerDepth = parser.getDepth(); 382 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 383 if (parser.getName().equals(TAG_WIFI_DISPLAY)) { 384 String deviceAddress = parser.getAttributeValue(null, ATTR_DEVICE_ADDRESS); 385 String deviceName = parser.getAttributeValue(null, ATTR_DEVICE_NAME); 386 String deviceAlias = parser.getAttributeValue(null, ATTR_DEVICE_ALIAS); 387 if (deviceAddress == null || deviceName == null) { 388 throw new XmlPullParserException( 389 "Missing deviceAddress or deviceName attribute on wifi-display."); 390 } 391 if (findRememberedWifiDisplay(deviceAddress) >= 0) { 392 throw new XmlPullParserException( 393 "Found duplicate wifi display device address."); 394 } 395 396 mRememberedWifiDisplays.add( 397 new WifiDisplay(deviceAddress, deviceName, deviceAlias, 398 false, false, false)); 399 } 400 } 401 } 402 loadDisplaysFromXml(XmlPullParser parser)403 private void loadDisplaysFromXml(XmlPullParser parser) 404 throws IOException, XmlPullParserException { 405 final int outerDepth = parser.getDepth(); 406 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 407 if (parser.getName().equals(TAG_DISPLAY)) { 408 String uniqueId = parser.getAttributeValue(null, ATTR_UNIQUE_ID); 409 if (uniqueId == null) { 410 throw new XmlPullParserException( 411 "Missing unique-id attribute on display."); 412 } 413 if (mDisplayStates.containsKey(uniqueId)) { 414 throw new XmlPullParserException("Found duplicate display."); 415 } 416 417 DisplayState state = new DisplayState(); 418 state.loadFromXml(parser); 419 mDisplayStates.put(uniqueId, state); 420 } 421 } 422 } 423 saveToXml(XmlSerializer serializer)424 private void saveToXml(XmlSerializer serializer) throws IOException { 425 serializer.startDocument(null, true); 426 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 427 serializer.startTag(null, TAG_DISPLAY_MANAGER_STATE); 428 serializer.startTag(null, TAG_REMEMBERED_WIFI_DISPLAYS); 429 for (WifiDisplay display : mRememberedWifiDisplays) { 430 serializer.startTag(null, TAG_WIFI_DISPLAY); 431 serializer.attribute(null, ATTR_DEVICE_ADDRESS, display.getDeviceAddress()); 432 serializer.attribute(null, ATTR_DEVICE_NAME, display.getDeviceName()); 433 if (display.getDeviceAlias() != null) { 434 serializer.attribute(null, ATTR_DEVICE_ALIAS, display.getDeviceAlias()); 435 } 436 serializer.endTag(null, TAG_WIFI_DISPLAY); 437 } 438 serializer.endTag(null, TAG_REMEMBERED_WIFI_DISPLAYS); 439 serializer.startTag(null, TAG_DISPLAY_STATES); 440 for (Map.Entry<String, DisplayState> entry : mDisplayStates.entrySet()) { 441 final String uniqueId = entry.getKey(); 442 final DisplayState state = entry.getValue(); 443 serializer.startTag(null, TAG_DISPLAY); 444 serializer.attribute(null, ATTR_UNIQUE_ID, uniqueId); 445 state.saveToXml(serializer); 446 serializer.endTag(null, TAG_DISPLAY); 447 } 448 serializer.endTag(null, TAG_DISPLAY_STATES); 449 serializer.startTag(null, TAG_STABLE_DEVICE_VALUES); 450 mStableDeviceValues.saveToXml(serializer); 451 serializer.endTag(null, TAG_STABLE_DEVICE_VALUES); 452 serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATIONS); 453 mBrightnessConfigurations.saveToXml(serializer); 454 serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATIONS); 455 serializer.endTag(null, TAG_DISPLAY_MANAGER_STATE); 456 serializer.endDocument(); 457 } 458 dump(PrintWriter pw)459 public void dump(PrintWriter pw) { 460 pw.println("PersistentDataStore"); 461 pw.println(" mLoaded=" + mLoaded); 462 pw.println(" mDirty=" + mDirty); 463 pw.println(" RememberedWifiDisplays:"); 464 int i = 0; 465 for (WifiDisplay display : mRememberedWifiDisplays) { 466 pw.println(" " + i++ + ": " + display); 467 } 468 pw.println(" DisplayStates:"); 469 i = 0; 470 for (Map.Entry<String, DisplayState> entry : mDisplayStates.entrySet()) { 471 pw.println(" " + i++ + ": " + entry.getKey()); 472 entry.getValue().dump(pw, " "); 473 } 474 pw.println(" StableDeviceValues:"); 475 mStableDeviceValues.dump(pw, " "); 476 pw.println(" BrightnessConfigurations:"); 477 mBrightnessConfigurations.dump(pw, " "); 478 } 479 480 private static final class DisplayState { 481 private int mColorMode; 482 setColorMode(int colorMode)483 public boolean setColorMode(int colorMode) { 484 if (colorMode == mColorMode) { 485 return false; 486 } 487 mColorMode = colorMode; 488 return true; 489 } 490 getColorMode()491 public int getColorMode() { 492 return mColorMode; 493 } 494 loadFromXml(XmlPullParser parser)495 public void loadFromXml(XmlPullParser parser) 496 throws IOException, XmlPullParserException { 497 final int outerDepth = parser.getDepth(); 498 499 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 500 if (parser.getName().equals(TAG_COLOR_MODE)) { 501 String value = parser.nextText(); 502 mColorMode = Integer.parseInt(value); 503 } 504 } 505 } 506 saveToXml(XmlSerializer serializer)507 public void saveToXml(XmlSerializer serializer) throws IOException { 508 serializer.startTag(null, TAG_COLOR_MODE); 509 serializer.text(Integer.toString(mColorMode)); 510 serializer.endTag(null, TAG_COLOR_MODE); 511 } 512 dump(final PrintWriter pw, final String prefix)513 public void dump(final PrintWriter pw, final String prefix) { 514 pw.println(prefix + "ColorMode=" + mColorMode); 515 } 516 } 517 518 private static final class StableDeviceValues { 519 private int mWidth; 520 private int mHeight; 521 getDisplaySize()522 private Point getDisplaySize() { 523 return new Point(mWidth, mHeight); 524 } 525 setDisplaySize(Point r)526 public boolean setDisplaySize(Point r) { 527 if (mWidth != r.x || mHeight != r.y) { 528 mWidth = r.x; 529 mHeight = r.y; 530 return true; 531 } 532 return false; 533 } 534 loadFromXml(XmlPullParser parser)535 public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException { 536 final int outerDepth = parser.getDepth(); 537 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 538 switch (parser.getName()) { 539 case TAG_STABLE_DISPLAY_WIDTH: 540 mWidth = loadIntValue(parser); 541 break; 542 case TAG_STABLE_DISPLAY_HEIGHT: 543 mHeight = loadIntValue(parser); 544 break; 545 } 546 } 547 } 548 loadIntValue(XmlPullParser parser)549 private static int loadIntValue(XmlPullParser parser) 550 throws IOException, XmlPullParserException { 551 try { 552 String value = parser.nextText(); 553 return Integer.parseInt(value); 554 } catch (NumberFormatException nfe) { 555 return 0; 556 } 557 } 558 saveToXml(XmlSerializer serializer)559 public void saveToXml(XmlSerializer serializer) throws IOException { 560 if (mWidth > 0 && mHeight > 0) { 561 serializer.startTag(null, TAG_STABLE_DISPLAY_WIDTH); 562 serializer.text(Integer.toString(mWidth)); 563 serializer.endTag(null, TAG_STABLE_DISPLAY_WIDTH); 564 serializer.startTag(null, TAG_STABLE_DISPLAY_HEIGHT); 565 serializer.text(Integer.toString(mHeight)); 566 serializer.endTag(null, TAG_STABLE_DISPLAY_HEIGHT); 567 } 568 } 569 dump(final PrintWriter pw, final String prefix)570 public void dump(final PrintWriter pw, final String prefix) { 571 pw.println(prefix + "StableDisplayWidth=" + mWidth); 572 pw.println(prefix + "StableDisplayHeight=" + mHeight); 573 } 574 } 575 576 private static final class BrightnessConfigurations { 577 // Maps from a user ID to the users' given brightness configuration 578 private SparseArray<BrightnessConfiguration> mConfigurations; 579 // Timestamp of time the configuration was set. 580 private SparseLongArray mTimeStamps; 581 // Package that set the configuration. 582 private SparseArray<String> mPackageNames; 583 BrightnessConfigurations()584 public BrightnessConfigurations() { 585 mConfigurations = new SparseArray<>(); 586 mTimeStamps = new SparseLongArray(); 587 mPackageNames = new SparseArray<>(); 588 } 589 setBrightnessConfigurationForUser(BrightnessConfiguration c, int userSerial, String packageName)590 private boolean setBrightnessConfigurationForUser(BrightnessConfiguration c, 591 int userSerial, String packageName) { 592 BrightnessConfiguration currentConfig = mConfigurations.get(userSerial); 593 if (currentConfig != c && (currentConfig == null || !currentConfig.equals(c))) { 594 if (c != null) { 595 if (packageName == null) { 596 mPackageNames.remove(userSerial); 597 } else { 598 mPackageNames.put(userSerial, packageName); 599 } 600 mTimeStamps.put(userSerial, System.currentTimeMillis()); 601 mConfigurations.put(userSerial, c); 602 } else { 603 mPackageNames.remove(userSerial); 604 mTimeStamps.delete(userSerial); 605 mConfigurations.remove(userSerial); 606 } 607 return true; 608 } 609 return false; 610 } 611 getBrightnessConfiguration(int userSerial)612 public BrightnessConfiguration getBrightnessConfiguration(int userSerial) { 613 return mConfigurations.get(userSerial); 614 } 615 loadFromXml(XmlPullParser parser)616 public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException { 617 final int outerDepth = parser.getDepth(); 618 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 619 if (TAG_BRIGHTNESS_CONFIGURATION.equals(parser.getName())) { 620 int userSerial; 621 try { 622 userSerial = Integer.parseInt( 623 parser.getAttributeValue(null, ATTR_USER_SERIAL)); 624 } catch (NumberFormatException nfe) { 625 userSerial = -1; 626 Slog.e(TAG, "Failed to read in brightness configuration", nfe); 627 } 628 629 String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); 630 String timeStampString = parser.getAttributeValue(null, ATTR_TIME_STAMP); 631 long timeStamp = -1; 632 if (timeStampString != null) { 633 try { 634 timeStamp = Long.parseLong(timeStampString); 635 } catch (NumberFormatException nfe) { 636 // Ignore we will just not restore the timestamp. 637 } 638 } 639 640 try { 641 BrightnessConfiguration config = 642 BrightnessConfiguration.loadFromXml(parser); 643 if (userSerial >= 0 && config != null) { 644 mConfigurations.put(userSerial, config); 645 if (timeStamp != -1) { 646 mTimeStamps.put(userSerial, timeStamp); 647 } 648 if (packageName != null) { 649 mPackageNames.put(userSerial, packageName); 650 } 651 } 652 } catch (IllegalArgumentException iae) { 653 Slog.e(TAG, "Failed to load brightness configuration!", iae); 654 } 655 } 656 } 657 } 658 saveToXml(XmlSerializer serializer)659 public void saveToXml(XmlSerializer serializer) throws IOException { 660 for (int i = 0; i < mConfigurations.size(); i++) { 661 final int userSerial = mConfigurations.keyAt(i); 662 final BrightnessConfiguration config = mConfigurations.valueAt(i); 663 664 serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATION); 665 serializer.attribute(null, ATTR_USER_SERIAL, Integer.toString(userSerial)); 666 String packageName = mPackageNames.get(userSerial); 667 if (packageName != null) { 668 serializer.attribute(null, ATTR_PACKAGE_NAME, packageName); 669 } 670 long timestamp = mTimeStamps.get(userSerial, -1); 671 if (timestamp != -1) { 672 serializer.attribute(null, ATTR_TIME_STAMP, Long.toString(timestamp)); 673 } 674 config.saveToXml(serializer); 675 serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATION); 676 } 677 } 678 dump(final PrintWriter pw, final String prefix)679 public void dump(final PrintWriter pw, final String prefix) { 680 for (int i = 0; i < mConfigurations.size(); i++) { 681 final int userSerial = mConfigurations.keyAt(i); 682 long time = mTimeStamps.get(userSerial, -1); 683 String packageName = mPackageNames.get(userSerial); 684 pw.println(prefix + "User " + userSerial + ":"); 685 if (time != -1) { 686 pw.println(prefix + " set at: " + TimeUtils.formatForLogging(time)); 687 } 688 if (packageName != null) { 689 pw.println(prefix + " set by: " + packageName); 690 } 691 pw.println(prefix + " " + mConfigurations.valueAt(i)); 692 } 693 } 694 } 695 696 @VisibleForTesting 697 static class Injector { 698 private final AtomicFile mAtomicFile; 699 Injector()700 public Injector() { 701 mAtomicFile = new AtomicFile(new File("/data/system/display-manager-state.xml"), 702 "display-state"); 703 } 704 openRead()705 public InputStream openRead() throws FileNotFoundException { 706 return mAtomicFile.openRead(); 707 } 708 startWrite()709 public OutputStream startWrite() throws IOException { 710 return mAtomicFile.startWrite(); 711 } 712 finishWrite(OutputStream os, boolean success)713 public void finishWrite(OutputStream os, boolean success) { 714 if (!(os instanceof FileOutputStream)) { 715 throw new IllegalArgumentException("Unexpected OutputStream as argument: " + os); 716 } 717 FileOutputStream fos = (FileOutputStream) os; 718 if (success) { 719 mAtomicFile.finishWrite(fos); 720 } else { 721 mAtomicFile.failWrite(fos); 722 } 723 } 724 } 725 } 726