Home | History | Annotate | Download | only in display
      1 /*
      2  * Copyright 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.android.server.display;
     18 
     19 import android.annotation.Nullable;
     20 import android.annotation.UserIdInt;
     21 import android.app.ActivityManager;
     22 import android.content.BroadcastReceiver;
     23 import android.content.ContentResolver;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.IntentFilter;
     27 import android.content.pm.ParceledListSlice;
     28 import android.database.ContentObserver;
     29 import android.hardware.Sensor;
     30 import android.hardware.SensorEvent;
     31 import android.hardware.SensorEventListener;
     32 import android.hardware.SensorManager;
     33 import android.hardware.display.AmbientBrightnessDayStats;
     34 import android.hardware.display.BrightnessChangeEvent;
     35 import android.net.Uri;
     36 import android.os.BatteryManager;
     37 import android.os.Environment;
     38 import android.os.Handler;
     39 import android.os.Looper;
     40 import android.os.Message;
     41 import android.os.PowerManager;
     42 import android.os.RemoteException;
     43 import android.os.SystemClock;
     44 import android.os.UserHandle;
     45 import android.os.UserManager;
     46 import android.provider.Settings;
     47 import android.util.AtomicFile;
     48 import android.util.Slog;
     49 import android.util.Xml;
     50 
     51 import com.android.internal.annotations.GuardedBy;
     52 import com.android.internal.annotations.VisibleForTesting;
     53 import com.android.internal.os.BackgroundThread;
     54 import com.android.internal.util.FastXmlSerializer;
     55 import com.android.internal.util.RingBuffer;
     56 
     57 import libcore.io.IoUtils;
     58 
     59 import org.xmlpull.v1.XmlPullParser;
     60 import org.xmlpull.v1.XmlPullParserException;
     61 import org.xmlpull.v1.XmlSerializer;
     62 
     63 import java.io.File;
     64 import java.io.FileInputStream;
     65 import java.io.FileOutputStream;
     66 import java.io.IOException;
     67 import java.io.InputStream;
     68 import java.io.OutputStream;
     69 import java.io.PrintWriter;
     70 import java.nio.charset.StandardCharsets;
     71 import java.text.SimpleDateFormat;
     72 import java.util.ArrayDeque;
     73 import java.util.ArrayList;
     74 import java.util.Collections;
     75 import java.util.Date;
     76 import java.util.Deque;
     77 import java.util.HashMap;
     78 import java.util.Map;
     79 import java.util.concurrent.TimeUnit;
     80 
     81 /**
     82  * Class that tracks recent brightness settings changes and stores
     83  * associated information such as light sensor readings.
     84  */
     85 public class BrightnessTracker {
     86 
     87     static final String TAG = "BrightnessTracker";
     88     static final boolean DEBUG = false;
     89 
     90     private static final String EVENTS_FILE = "brightness_events.xml";
     91     private static final String AMBIENT_BRIGHTNESS_STATS_FILE = "ambient_brightness_stats.xml";
     92     private static final int MAX_EVENTS = 100;
     93     // Discard events when reading or writing that are older than this.
     94     private static final long MAX_EVENT_AGE = TimeUnit.DAYS.toMillis(30);
     95     // Time over which we keep lux sensor readings.
     96     private static final long LUX_EVENT_HORIZON = TimeUnit.SECONDS.toNanos(10);
     97 
     98     private static final String TAG_EVENTS = "events";
     99     private static final String TAG_EVENT = "event";
    100     private static final String ATTR_NITS = "nits";
    101     private static final String ATTR_TIMESTAMP = "timestamp";
    102     private static final String ATTR_PACKAGE_NAME = "packageName";
    103     private static final String ATTR_USER = "user";
    104     private static final String ATTR_LUX = "lux";
    105     private static final String ATTR_LUX_TIMESTAMPS = "luxTimestamps";
    106     private static final String ATTR_BATTERY_LEVEL = "batteryLevel";
    107     private static final String ATTR_NIGHT_MODE = "nightMode";
    108     private static final String ATTR_COLOR_TEMPERATURE = "colorTemperature";
    109     private static final String ATTR_LAST_NITS = "lastNits";
    110     private static final String ATTR_DEFAULT_CONFIG = "defaultConfig";
    111     private static final String ATTR_POWER_SAVE = "powerSaveFactor";
    112     private static final String ATTR_USER_POINT = "userPoint";
    113 
    114     private static final int MSG_BACKGROUND_START = 0;
    115     private static final int MSG_BRIGHTNESS_CHANGED = 1;
    116     private static final int MSG_STOP_SENSOR_LISTENER = 2;
    117     private static final int MSG_START_SENSOR_LISTENER = 3;
    118 
    119     private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
    120 
    121     // Lock held while accessing mEvents, is held while writing events to flash.
    122     private final Object mEventsLock = new Object();
    123     @GuardedBy("mEventsLock")
    124     private RingBuffer<BrightnessChangeEvent> mEvents
    125             = new RingBuffer<>(BrightnessChangeEvent.class, MAX_EVENTS);
    126     @GuardedBy("mEventsLock")
    127     private boolean mEventsDirty;
    128 
    129     private volatile boolean mWriteBrightnessTrackerStateScheduled;
    130 
    131     private AmbientBrightnessStatsTracker mAmbientBrightnessStatsTracker;
    132 
    133     private final UserManager mUserManager;
    134     private final Context mContext;
    135     private final ContentResolver mContentResolver;
    136     private final Handler mBgHandler;
    137 
    138     // mBroadcastReceiver,  mSensorListener, mSettingsObserver and mSensorRegistered
    139     // should only be used on the mBgHandler thread.
    140     private BroadcastReceiver mBroadcastReceiver;
    141     private SensorListener mSensorListener;
    142     private SettingsObserver mSettingsObserver;
    143     private boolean mSensorRegistered;
    144 
    145     private @UserIdInt int mCurrentUserId = UserHandle.USER_NULL;
    146 
    147     // Lock held while collecting data related to brightness changes.
    148     private final Object mDataCollectionLock = new Object();
    149     @GuardedBy("mDataCollectionLock")
    150     private Deque<LightData> mLastSensorReadings = new ArrayDeque<>();
    151     @GuardedBy("mDataCollectionLock")
    152     private float mLastBatteryLevel = Float.NaN;
    153     @GuardedBy("mDataCollectionLock")
    154     private float mLastBrightness = -1;
    155     @GuardedBy("mDataCollectionLock")
    156     private boolean mStarted;
    157 
    158     private final Injector mInjector;
    159 
    160     public BrightnessTracker(Context context, @Nullable Injector injector) {
    161         // Note this will be called very early in boot, other system
    162         // services may not be present.
    163         mContext = context;
    164         mContentResolver = context.getContentResolver();
    165         if (injector != null) {
    166             mInjector = injector;
    167         } else {
    168             mInjector = new Injector();
    169         }
    170         mBgHandler = new TrackerHandler(mInjector.getBackgroundHandler().getLooper());
    171         mUserManager = mContext.getSystemService(UserManager.class);
    172     }
    173 
    174     /**
    175      * Start listening for brightness slider events
    176      *
    177      * @param initialBrightness the initial screen brightness
    178      */
    179     public void start(float initialBrightness) {
    180         if (DEBUG) {
    181             Slog.d(TAG, "Start");
    182         }
    183         mCurrentUserId = ActivityManager.getCurrentUser();
    184         mBgHandler.obtainMessage(MSG_BACKGROUND_START, (Float) initialBrightness).sendToTarget();
    185     }
    186 
    187     private void backgroundStart(float initialBrightness) {
    188         readEvents();
    189         readAmbientBrightnessStats();
    190 
    191         mSensorListener = new SensorListener();
    192 
    193         mSettingsObserver = new SettingsObserver(mBgHandler);
    194         mInjector.registerBrightnessModeObserver(mContentResolver, mSettingsObserver);
    195         startSensorListener();
    196 
    197         final IntentFilter intentFilter = new IntentFilter();
    198         intentFilter.addAction(Intent.ACTION_SHUTDOWN);
    199         intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
    200         intentFilter.addAction(Intent.ACTION_SCREEN_ON);
    201         intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
    202         mBroadcastReceiver = new Receiver();
    203         mInjector.registerReceiver(mContext, mBroadcastReceiver, intentFilter);
    204 
    205         mInjector.scheduleIdleJob(mContext);
    206         synchronized (mDataCollectionLock) {
    207             mLastBrightness = initialBrightness;
    208             mStarted = true;
    209         }
    210     }
    211 
    212     /** Stop listening for events */
    213     @VisibleForTesting
    214     void stop() {
    215         if (DEBUG) {
    216             Slog.d(TAG, "Stop");
    217         }
    218         mBgHandler.removeMessages(MSG_BACKGROUND_START);
    219         stopSensorListener();
    220         mInjector.unregisterSensorListener(mContext, mSensorListener);
    221         mInjector.unregisterBrightnessModeObserver(mContext, mSettingsObserver);
    222         mInjector.unregisterReceiver(mContext, mBroadcastReceiver);
    223         mInjector.cancelIdleJob(mContext);
    224 
    225         synchronized (mDataCollectionLock) {
    226             mStarted = false;
    227         }
    228     }
    229 
    230     public void onSwitchUser(@UserIdInt int newUserId) {
    231         if (DEBUG) {
    232             Slog.d(TAG, "Used id updated from " + mCurrentUserId + " to " + newUserId);
    233         }
    234         mCurrentUserId = newUserId;
    235     }
    236 
    237     /**
    238      * @param userId userId to fetch data for.
    239      * @param includePackage if false we will null out BrightnessChangeEvent.packageName
    240      * @return List of recent {@link BrightnessChangeEvent}s
    241      */
    242     public ParceledListSlice<BrightnessChangeEvent> getEvents(int userId, boolean includePackage) {
    243         BrightnessChangeEvent[] events;
    244         synchronized (mEventsLock) {
    245             events = mEvents.toArray();
    246         }
    247         int[] profiles = mInjector.getProfileIds(mUserManager, userId);
    248         Map<Integer, Boolean> toRedact = new HashMap<>();
    249         for (int i = 0; i < profiles.length; ++i) {
    250             int profileId = profiles[i];
    251             // Include slider interactions when a managed profile app is in the
    252             // foreground but always redact the package name.
    253             boolean redact = (!includePackage) || profileId != userId;
    254             toRedact.put(profiles[i], redact);
    255         }
    256         ArrayList<BrightnessChangeEvent> out = new ArrayList<>(events.length);
    257         for (int i = 0; i < events.length; ++i) {
    258             Boolean redact = toRedact.get(events[i].userId);
    259             if (redact != null) {
    260                 if (!redact) {
    261                     out.add(events[i]);
    262                 } else {
    263                     BrightnessChangeEvent event = new BrightnessChangeEvent((events[i]),
    264                             /* redactPackage */ true);
    265                     out.add(event);
    266                 }
    267             }
    268         }
    269         return new ParceledListSlice<>(out);
    270     }
    271 
    272     public void persistBrightnessTrackerState() {
    273         scheduleWriteBrightnessTrackerState();
    274     }
    275 
    276     /**
    277      * Notify the BrightnessTracker that the user has changed the brightness of the display.
    278      */
    279     public void notifyBrightnessChanged(float brightness, boolean userInitiated,
    280             float powerBrightnessFactor, boolean isUserSetBrightness,
    281             boolean isDefaultBrightnessConfig) {
    282         if (DEBUG) {
    283             Slog.d(TAG, String.format("notifyBrightnessChanged(brightness=%f, userInitiated=%b)",
    284                         brightness, userInitiated));
    285         }
    286         Message m = mBgHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED,
    287                 userInitiated ? 1 : 0, 0 /*unused*/, new BrightnessChangeValues(brightness,
    288                         powerBrightnessFactor, isUserSetBrightness, isDefaultBrightnessConfig,
    289                         mInjector.currentTimeMillis()));
    290         m.sendToTarget();
    291     }
    292 
    293     private void handleBrightnessChanged(float brightness, boolean userInitiated,
    294             float powerBrightnessFactor, boolean isUserSetBrightness,
    295             boolean isDefaultBrightnessConfig, long timestamp) {
    296         BrightnessChangeEvent.Builder builder;
    297 
    298         synchronized (mDataCollectionLock) {
    299             if (!mStarted) {
    300                 // Not currently gathering brightness change information
    301                 return;
    302             }
    303 
    304             float previousBrightness = mLastBrightness;
    305             mLastBrightness = brightness;
    306 
    307             if (!userInitiated) {
    308                 // We want to record what current brightness is so that we know what the user
    309                 // changed it from, but if it wasn't user initiated then we don't want to record it
    310                 // as a BrightnessChangeEvent.
    311                 return;
    312             }
    313 
    314             builder = new BrightnessChangeEvent.Builder();
    315             builder.setBrightness(brightness);
    316             builder.setTimeStamp(timestamp);
    317             builder.setPowerBrightnessFactor(powerBrightnessFactor);
    318             builder.setUserBrightnessPoint(isUserSetBrightness);
    319             builder.setIsDefaultBrightnessConfig(isDefaultBrightnessConfig);
    320 
    321             final int readingCount = mLastSensorReadings.size();
    322             if (readingCount == 0) {
    323                 // No sensor data so ignore this.
    324                 return;
    325             }
    326 
    327             float[] luxValues = new float[readingCount];
    328             long[] luxTimestamps = new long[readingCount];
    329 
    330             int pos = 0;
    331 
    332             // Convert sensor timestamp in elapsed time nanos to current time millis.
    333             long currentTimeMillis = mInjector.currentTimeMillis();
    334             long elapsedTimeNanos = mInjector.elapsedRealtimeNanos();
    335             for (LightData reading : mLastSensorReadings) {
    336                 luxValues[pos] = reading.lux;
    337                 luxTimestamps[pos] = currentTimeMillis -
    338                         TimeUnit.NANOSECONDS.toMillis(elapsedTimeNanos - reading.timestamp);
    339                 ++pos;
    340             }
    341             builder.setLuxValues(luxValues);
    342             builder.setLuxTimestamps(luxTimestamps);
    343 
    344             builder.setBatteryLevel(mLastBatteryLevel);
    345             builder.setLastBrightness(previousBrightness);
    346         }
    347 
    348         try {
    349             final ActivityManager.StackInfo focusedStack = mInjector.getFocusedStack();
    350             if (focusedStack != null && focusedStack.topActivity != null) {
    351                 builder.setUserId(focusedStack.userId);
    352                 builder.setPackageName(focusedStack.topActivity.getPackageName());
    353             } else {
    354                 // Ignore the event because we can't determine user / package.
    355                 if (DEBUG) {
    356                     Slog.d(TAG, "Ignoring event due to null focusedStack.");
    357                 }
    358                 return;
    359             }
    360         } catch (RemoteException e) {
    361             // Really shouldn't be possible.
    362             return;
    363         }
    364 
    365         builder.setNightMode(mInjector.getSecureIntForUser(mContentResolver,
    366                 Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 0, UserHandle.USER_CURRENT)
    367                 == 1);
    368         builder.setColorTemperature(mInjector.getSecureIntForUser(mContentResolver,
    369                 Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
    370                 0, UserHandle.USER_CURRENT));
    371 
    372         BrightnessChangeEvent event = builder.build();
    373         if (DEBUG) {
    374             Slog.d(TAG, "Event " + event.brightness + " " + event.packageName);
    375         }
    376         synchronized (mEventsLock) {
    377             mEventsDirty = true;
    378             mEvents.append(event);
    379         }
    380     }
    381 
    382     private void startSensorListener() {
    383         if (!mSensorRegistered
    384                 && mInjector.isInteractive(mContext)
    385                 && mInjector.isBrightnessModeAutomatic(mContentResolver)) {
    386             mAmbientBrightnessStatsTracker.start();
    387             mSensorRegistered = true;
    388             mInjector.registerSensorListener(mContext, mSensorListener,
    389                     mInjector.getBackgroundHandler());
    390         }
    391     }
    392 
    393     private void stopSensorListener() {
    394         if (mSensorRegistered) {
    395             mAmbientBrightnessStatsTracker.stop();
    396             mInjector.unregisterSensorListener(mContext, mSensorListener);
    397             mSensorRegistered = false;
    398         }
    399     }
    400 
    401     private void scheduleWriteBrightnessTrackerState() {
    402         if (!mWriteBrightnessTrackerStateScheduled) {
    403             mBgHandler.post(() -> {
    404                 mWriteBrightnessTrackerStateScheduled = false;
    405                 writeEvents();
    406                 writeAmbientBrightnessStats();
    407             });
    408             mWriteBrightnessTrackerStateScheduled = true;
    409         }
    410     }
    411 
    412     private void writeEvents() {
    413         synchronized (mEventsLock) {
    414             if (!mEventsDirty) {
    415                 // Nothing to write
    416                 return;
    417             }
    418 
    419             final AtomicFile writeTo = mInjector.getFile(EVENTS_FILE);
    420             if (writeTo == null) {
    421                 return;
    422             }
    423             if (mEvents.isEmpty()) {
    424                 if (writeTo.exists()) {
    425                     writeTo.delete();
    426                 }
    427                 mEventsDirty = false;
    428             } else {
    429                 FileOutputStream output = null;
    430                 try {
    431                     output = writeTo.startWrite();
    432                     writeEventsLocked(output);
    433                     writeTo.finishWrite(output);
    434                     mEventsDirty = false;
    435                 } catch (IOException e) {
    436                     writeTo.failWrite(output);
    437                     Slog.e(TAG, "Failed to write change mEvents.", e);
    438                 }
    439             }
    440         }
    441     }
    442 
    443     private void writeAmbientBrightnessStats() {
    444         final AtomicFile writeTo = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE);
    445         if (writeTo == null) {
    446             return;
    447         }
    448         FileOutputStream output = null;
    449         try {
    450             output = writeTo.startWrite();
    451             mAmbientBrightnessStatsTracker.writeStats(output);
    452             writeTo.finishWrite(output);
    453         } catch (IOException e) {
    454             writeTo.failWrite(output);
    455             Slog.e(TAG, "Failed to write ambient brightness stats.", e);
    456         }
    457     }
    458 
    459     private void readEvents() {
    460         synchronized (mEventsLock) {
    461             // Read might prune events so mark as dirty.
    462             mEventsDirty = true;
    463             mEvents.clear();
    464             final AtomicFile readFrom = mInjector.getFile(EVENTS_FILE);
    465             if (readFrom != null && readFrom.exists()) {
    466                 FileInputStream input = null;
    467                 try {
    468                     input = readFrom.openRead();
    469                     readEventsLocked(input);
    470                 } catch (IOException e) {
    471                     readFrom.delete();
    472                     Slog.e(TAG, "Failed to read change mEvents.", e);
    473                 } finally {
    474                     IoUtils.closeQuietly(input);
    475                 }
    476             }
    477         }
    478     }
    479 
    480     private void readAmbientBrightnessStats() {
    481         mAmbientBrightnessStatsTracker = new AmbientBrightnessStatsTracker(mUserManager, null);
    482         final AtomicFile readFrom = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE);
    483         if (readFrom != null && readFrom.exists()) {
    484             FileInputStream input = null;
    485             try {
    486                 input = readFrom.openRead();
    487                 mAmbientBrightnessStatsTracker.readStats(input);
    488             } catch (IOException e) {
    489                 readFrom.delete();
    490                 Slog.e(TAG, "Failed to read ambient brightness stats.", e);
    491             } finally {
    492                 IoUtils.closeQuietly(input);
    493             }
    494         }
    495     }
    496 
    497     @VisibleForTesting
    498     @GuardedBy("mEventsLock")
    499     void writeEventsLocked(OutputStream stream) throws IOException {
    500         XmlSerializer out = new FastXmlSerializer();
    501         out.setOutput(stream, StandardCharsets.UTF_8.name());
    502         out.startDocument(null, true);
    503         out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
    504 
    505         out.startTag(null, TAG_EVENTS);
    506         BrightnessChangeEvent[] toWrite = mEvents.toArray();
    507         // Clear events, code below will add back the ones that are still within the time window.
    508         mEvents.clear();
    509         if (DEBUG) {
    510             Slog.d(TAG, "Writing events " + toWrite.length);
    511         }
    512         final long timeCutOff = mInjector.currentTimeMillis() - MAX_EVENT_AGE;
    513         for (int i = 0; i < toWrite.length; ++i) {
    514             int userSerialNo = mInjector.getUserSerialNumber(mUserManager, toWrite[i].userId);
    515             if (userSerialNo != -1 && toWrite[i].timeStamp > timeCutOff) {
    516                 mEvents.append(toWrite[i]);
    517                 out.startTag(null, TAG_EVENT);
    518                 out.attribute(null, ATTR_NITS, Float.toString(toWrite[i].brightness));
    519                 out.attribute(null, ATTR_TIMESTAMP, Long.toString(toWrite[i].timeStamp));
    520                 out.attribute(null, ATTR_PACKAGE_NAME, toWrite[i].packageName);
    521                 out.attribute(null, ATTR_USER, Integer.toString(userSerialNo));
    522                 out.attribute(null, ATTR_BATTERY_LEVEL, Float.toString(toWrite[i].batteryLevel));
    523                 out.attribute(null, ATTR_NIGHT_MODE, Boolean.toString(toWrite[i].nightMode));
    524                 out.attribute(null, ATTR_COLOR_TEMPERATURE, Integer.toString(
    525                         toWrite[i].colorTemperature));
    526                 out.attribute(null, ATTR_LAST_NITS,
    527                         Float.toString(toWrite[i].lastBrightness));
    528                 out.attribute(null, ATTR_DEFAULT_CONFIG,
    529                         Boolean.toString(toWrite[i].isDefaultBrightnessConfig));
    530                 out.attribute(null, ATTR_POWER_SAVE,
    531                         Float.toString(toWrite[i].powerBrightnessFactor));
    532                 out.attribute(null, ATTR_USER_POINT,
    533                         Boolean.toString(toWrite[i].isUserSetBrightness));
    534                 StringBuilder luxValues = new StringBuilder();
    535                 StringBuilder luxTimestamps = new StringBuilder();
    536                 for (int j = 0; j < toWrite[i].luxValues.length; ++j) {
    537                     if (j > 0) {
    538                         luxValues.append(',');
    539                         luxTimestamps.append(',');
    540                     }
    541                     luxValues.append(Float.toString(toWrite[i].luxValues[j]));
    542                     luxTimestamps.append(Long.toString(toWrite[i].luxTimestamps[j]));
    543                 }
    544                 out.attribute(null, ATTR_LUX, luxValues.toString());
    545                 out.attribute(null, ATTR_LUX_TIMESTAMPS, luxTimestamps.toString());
    546                 out.endTag(null, TAG_EVENT);
    547             }
    548         }
    549         out.endTag(null, TAG_EVENTS);
    550         out.endDocument();
    551         stream.flush();
    552     }
    553 
    554     @VisibleForTesting
    555     @GuardedBy("mEventsLock")
    556     void readEventsLocked(InputStream stream) throws IOException {
    557         try {
    558             XmlPullParser parser = Xml.newPullParser();
    559             parser.setInput(stream, StandardCharsets.UTF_8.name());
    560 
    561             int type;
    562             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
    563                     && type != XmlPullParser.START_TAG) {
    564             }
    565             String tag = parser.getName();
    566             if (!TAG_EVENTS.equals(tag)) {
    567                 throw new XmlPullParserException(
    568                         "Events not found in brightness tracker file " + tag);
    569             }
    570 
    571             final long timeCutOff = mInjector.currentTimeMillis() - MAX_EVENT_AGE;
    572 
    573             parser.next();
    574             int outerDepth = parser.getDepth();
    575             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
    576                     && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
    577                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
    578                     continue;
    579                 }
    580                 tag = parser.getName();
    581                 if (TAG_EVENT.equals(tag)) {
    582                     BrightnessChangeEvent.Builder builder = new BrightnessChangeEvent.Builder();
    583 
    584                     String brightness = parser.getAttributeValue(null, ATTR_NITS);
    585                     builder.setBrightness(Float.parseFloat(brightness));
    586                     String timestamp = parser.getAttributeValue(null, ATTR_TIMESTAMP);
    587                     builder.setTimeStamp(Long.parseLong(timestamp));
    588                     builder.setPackageName(parser.getAttributeValue(null, ATTR_PACKAGE_NAME));
    589                     String user = parser.getAttributeValue(null, ATTR_USER);
    590                     builder.setUserId(mInjector.getUserId(mUserManager, Integer.parseInt(user)));
    591                     String batteryLevel = parser.getAttributeValue(null, ATTR_BATTERY_LEVEL);
    592                     builder.setBatteryLevel(Float.parseFloat(batteryLevel));
    593                     String nightMode = parser.getAttributeValue(null, ATTR_NIGHT_MODE);
    594                     builder.setNightMode(Boolean.parseBoolean(nightMode));
    595                     String colorTemperature =
    596                             parser.getAttributeValue(null, ATTR_COLOR_TEMPERATURE);
    597                     builder.setColorTemperature(Integer.parseInt(colorTemperature));
    598                     String lastBrightness = parser.getAttributeValue(null, ATTR_LAST_NITS);
    599                     builder.setLastBrightness(Float.parseFloat(lastBrightness));
    600 
    601                     String luxValue = parser.getAttributeValue(null, ATTR_LUX);
    602                     String luxTimestamp = parser.getAttributeValue(null, ATTR_LUX_TIMESTAMPS);
    603 
    604                     String[] luxValuesStrings = luxValue.split(",");
    605                     String[] luxTimestampsStrings = luxTimestamp.split(",");
    606                     if (luxValuesStrings.length != luxTimestampsStrings.length) {
    607                         continue;
    608                     }
    609                     float[] luxValues = new float[luxValuesStrings.length];
    610                     long[] luxTimestamps = new long[luxValuesStrings.length];
    611                     for (int i = 0; i < luxValues.length; ++i) {
    612                         luxValues[i] = Float.parseFloat(luxValuesStrings[i]);
    613                         luxTimestamps[i] = Long.parseLong(luxTimestampsStrings[i]);
    614                     }
    615                     builder.setLuxValues(luxValues);
    616                     builder.setLuxTimestamps(luxTimestamps);
    617 
    618                     String defaultConfig = parser.getAttributeValue(null, ATTR_DEFAULT_CONFIG);
    619                     if (defaultConfig != null) {
    620                         builder.setIsDefaultBrightnessConfig(Boolean.parseBoolean(defaultConfig));
    621                     }
    622                     String powerSave = parser.getAttributeValue(null, ATTR_POWER_SAVE);
    623                     if (powerSave != null) {
    624                         builder.setPowerBrightnessFactor(Float.parseFloat(powerSave));
    625                     } else {
    626                         builder.setPowerBrightnessFactor(1.0f);
    627                     }
    628                     String userPoint = parser.getAttributeValue(null, ATTR_USER_POINT);
    629                     if (userPoint != null) {
    630                         builder.setUserBrightnessPoint(Boolean.parseBoolean(userPoint));
    631                     }
    632 
    633                     BrightnessChangeEvent event = builder.build();
    634                     if (DEBUG) {
    635                         Slog.i(TAG, "Read event " + event.brightness
    636                                 + " " + event.packageName);
    637                     }
    638 
    639                     if (event.userId != -1 && event.timeStamp > timeCutOff
    640                             && event.luxValues.length > 0) {
    641                         mEvents.append(event);
    642                     }
    643                 }
    644             }
    645         } catch (NullPointerException | NumberFormatException | XmlPullParserException
    646                 | IOException e) {
    647             // Failed to parse something, just start with an empty event log.
    648             mEvents = new RingBuffer<>(BrightnessChangeEvent.class, MAX_EVENTS);
    649             Slog.e(TAG, "Failed to parse brightness event", e);
    650             // Re-throw so we will delete the bad file.
    651             throw new IOException("failed to parse file", e);
    652         }
    653     }
    654 
    655     public void dump(final PrintWriter pw) {
    656         pw.println("BrightnessTracker state:");
    657         synchronized (mDataCollectionLock) {
    658             pw.println("  mStarted=" + mStarted);
    659             pw.println("  mLastBatteryLevel=" + mLastBatteryLevel);
    660             pw.println("  mLastBrightness=" + mLastBrightness);
    661             pw.println("  mLastSensorReadings.size=" + mLastSensorReadings.size());
    662             if (!mLastSensorReadings.isEmpty()) {
    663                 pw.println("  mLastSensorReadings time span "
    664                         + mLastSensorReadings.peekFirst().timestamp + "->"
    665                         + mLastSensorReadings.peekLast().timestamp);
    666             }
    667         }
    668         synchronized (mEventsLock) {
    669             pw.println("  mEventsDirty=" + mEventsDirty);
    670             pw.println("  mEvents.size=" + mEvents.size());
    671             BrightnessChangeEvent[] events = mEvents.toArray();
    672             for (int i = 0; i < events.length; ++i) {
    673                 pw.print("    " + FORMAT.format(new Date(events[i].timeStamp)));
    674                 pw.print(", userId=" + events[i].userId);
    675                 pw.print(", " + events[i].lastBrightness + "->" + events[i].brightness);
    676                 pw.print(", isUserSetBrightness=" + events[i].isUserSetBrightness);
    677                 pw.print(", powerBrightnessFactor=" + events[i].powerBrightnessFactor);
    678                 pw.print(", isDefaultBrightnessConfig=" + events[i].isDefaultBrightnessConfig);
    679                 pw.print(" {");
    680                 for (int j = 0; j < events[i].luxValues.length; ++j){
    681                     if (j != 0) {
    682                         pw.print(", ");
    683                     }
    684                     pw.print("(" + events[i].luxValues[j] + "," + events[i].luxTimestamps[j] + ")");
    685                 }
    686                 pw.println("}");
    687             }
    688         }
    689         pw.println("  mWriteBrightnessTrackerStateScheduled="
    690                 + mWriteBrightnessTrackerStateScheduled);
    691         mBgHandler.runWithScissors(() -> dumpLocal(pw), 1000);
    692         if (mAmbientBrightnessStatsTracker != null) {
    693             pw.println();
    694             mAmbientBrightnessStatsTracker.dump(pw);
    695         }
    696     }
    697 
    698     private void dumpLocal(PrintWriter pw) {
    699         pw.println("  mSensorRegistered=" + mSensorRegistered);
    700     }
    701 
    702     public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(int userId) {
    703         if (mAmbientBrightnessStatsTracker != null) {
    704             ArrayList<AmbientBrightnessDayStats> stats =
    705                     mAmbientBrightnessStatsTracker.getUserStats(userId);
    706             if (stats != null) {
    707                 return new ParceledListSlice<>(stats);
    708             }
    709         }
    710         return ParceledListSlice.emptyList();
    711     }
    712 
    713     // Not allowed to keep the SensorEvent so used to copy the data we care about.
    714     private static class LightData {
    715         public float lux;
    716         // Time in elapsedRealtimeNanos
    717         public long timestamp;
    718     }
    719 
    720     private void recordSensorEvent(SensorEvent event) {
    721         long horizon = mInjector.elapsedRealtimeNanos() - LUX_EVENT_HORIZON;
    722         synchronized (mDataCollectionLock) {
    723             if (DEBUG) {
    724                 Slog.v(TAG, "Sensor event " + event);
    725             }
    726             if (!mLastSensorReadings.isEmpty()
    727                     && event.timestamp < mLastSensorReadings.getLast().timestamp) {
    728                 // Ignore event that came out of order.
    729                 return;
    730             }
    731             LightData data = null;
    732             while (!mLastSensorReadings.isEmpty()
    733                     && mLastSensorReadings.getFirst().timestamp < horizon) {
    734                 // Remove data that has fallen out of the window.
    735                 data = mLastSensorReadings.removeFirst();
    736             }
    737             // We put back the last one we removed so we know how long
    738             // the first sensor reading was valid for.
    739             if (data != null) {
    740                 mLastSensorReadings.addFirst(data);
    741             }
    742 
    743             data = new LightData();
    744             data.timestamp = event.timestamp;
    745             data.lux = event.values[0];
    746             mLastSensorReadings.addLast(data);
    747         }
    748     }
    749 
    750     private void recordAmbientBrightnessStats(SensorEvent event) {
    751         mAmbientBrightnessStatsTracker.add(mCurrentUserId, event.values[0]);
    752     }
    753 
    754     private void batteryLevelChanged(int level, int scale) {
    755         synchronized (mDataCollectionLock) {
    756             mLastBatteryLevel = (float) level / (float) scale;
    757         }
    758     }
    759 
    760     private final class SensorListener implements SensorEventListener {
    761         @Override
    762         public void onSensorChanged(SensorEvent event) {
    763             recordSensorEvent(event);
    764             recordAmbientBrightnessStats(event);
    765         }
    766 
    767         @Override
    768         public void onAccuracyChanged(Sensor sensor, int accuracy) {
    769 
    770         }
    771     }
    772 
    773     private final class SettingsObserver extends ContentObserver {
    774         public SettingsObserver(Handler handler) {
    775             super(handler);
    776         }
    777 
    778         @Override
    779         public void onChange(boolean selfChange, Uri uri) {
    780             if (DEBUG) {
    781                 Slog.v(TAG, "settings change " + uri);
    782             }
    783             if (mInjector.isBrightnessModeAutomatic(mContentResolver)) {
    784                 mBgHandler.obtainMessage(MSG_START_SENSOR_LISTENER).sendToTarget();
    785             } else {
    786                 mBgHandler.obtainMessage(MSG_STOP_SENSOR_LISTENER).sendToTarget();
    787             }
    788         }
    789     }
    790 
    791     private final class Receiver extends BroadcastReceiver {
    792         @Override
    793         public void onReceive(Context context, Intent intent) {
    794             if (DEBUG) {
    795                 Slog.d(TAG, "Received " + intent.getAction());
    796             }
    797             String action = intent.getAction();
    798             if (Intent.ACTION_SHUTDOWN.equals(action)) {
    799                 stop();
    800                 scheduleWriteBrightnessTrackerState();
    801             } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
    802                 int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
    803                 int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
    804                 if (level != -1 && scale != 0) {
    805                     batteryLevelChanged(level, scale);
    806                 }
    807             } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
    808                 mBgHandler.obtainMessage(MSG_STOP_SENSOR_LISTENER).sendToTarget();
    809             } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
    810                 mBgHandler.obtainMessage(MSG_START_SENSOR_LISTENER).sendToTarget();
    811             }
    812         }
    813     }
    814 
    815     private final class TrackerHandler extends Handler {
    816         public TrackerHandler(Looper looper) {
    817             super(looper, null, true /*async*/);
    818         }
    819         public void handleMessage(Message msg) {
    820             switch (msg.what) {
    821                 case MSG_BACKGROUND_START:
    822                     backgroundStart((float)msg.obj /*initial brightness*/);
    823                     break;
    824                 case MSG_BRIGHTNESS_CHANGED:
    825                     BrightnessChangeValues values = (BrightnessChangeValues) msg.obj;
    826                     boolean userInitiatedChange = (msg.arg1 == 1);
    827                     handleBrightnessChanged(values.brightness, userInitiatedChange,
    828                             values.powerBrightnessFactor, values.isUserSetBrightness,
    829                             values.isDefaultBrightnessConfig, values.timestamp);
    830                     break;
    831                 case MSG_START_SENSOR_LISTENER:
    832                     startSensorListener();
    833                     break;
    834                 case MSG_STOP_SENSOR_LISTENER:
    835                     stopSensorListener();
    836                     break;
    837             }
    838         }
    839     }
    840 
    841     private static class BrightnessChangeValues {
    842         final float brightness;
    843         final float powerBrightnessFactor;
    844         final boolean isUserSetBrightness;
    845         final boolean isDefaultBrightnessConfig;
    846         final long timestamp;
    847 
    848         BrightnessChangeValues(float brightness, float powerBrightnessFactor,
    849                 boolean isUserSetBrightness, boolean isDefaultBrightnessConfig,
    850                 long timestamp) {
    851             this.brightness = brightness;
    852             this.powerBrightnessFactor = powerBrightnessFactor;
    853             this.isUserSetBrightness = isUserSetBrightness;
    854             this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
    855             this.timestamp = timestamp;
    856         }
    857     }
    858 
    859     @VisibleForTesting
    860     static class Injector {
    861         public void registerSensorListener(Context context,
    862                 SensorEventListener sensorListener, Handler handler) {
    863             SensorManager sensorManager = context.getSystemService(SensorManager.class);
    864             Sensor lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
    865             sensorManager.registerListener(sensorListener,
    866                     lightSensor, SensorManager.SENSOR_DELAY_NORMAL, handler);
    867         }
    868 
    869         public void unregisterSensorListener(Context context, SensorEventListener sensorListener) {
    870             SensorManager sensorManager = context.getSystemService(SensorManager.class);
    871             sensorManager.unregisterListener(sensorListener);
    872         }
    873 
    874         public void registerBrightnessModeObserver(ContentResolver resolver,
    875                 ContentObserver settingsObserver) {
    876             resolver.registerContentObserver(Settings.System.getUriFor(
    877                     Settings.System.SCREEN_BRIGHTNESS_MODE),
    878                     false, settingsObserver, UserHandle.USER_ALL);
    879         }
    880 
    881         public void unregisterBrightnessModeObserver(Context context,
    882                 ContentObserver settingsObserver) {
    883             context.getContentResolver().unregisterContentObserver(settingsObserver);
    884         }
    885 
    886         public void registerReceiver(Context context,
    887                 BroadcastReceiver receiver, IntentFilter filter) {
    888             context.registerReceiver(receiver, filter);
    889         }
    890 
    891         public void unregisterReceiver(Context context,
    892                 BroadcastReceiver receiver) {
    893             context.unregisterReceiver(receiver);
    894         }
    895 
    896         public Handler getBackgroundHandler() {
    897             return BackgroundThread.getHandler();
    898         }
    899 
    900         public boolean isBrightnessModeAutomatic(ContentResolver resolver) {
    901             return Settings.System.getIntForUser(resolver, Settings.System.SCREEN_BRIGHTNESS_MODE,
    902                     Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT)
    903                     == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
    904         }
    905 
    906         public int getSecureIntForUser(ContentResolver resolver, String setting, int defaultValue,
    907                 int userId) {
    908             return Settings.Secure.getIntForUser(resolver, setting, defaultValue, userId);
    909         }
    910 
    911         public AtomicFile getFile(String filename) {
    912             return new AtomicFile(new File(Environment.getDataSystemDeDirectory(), filename));
    913         }
    914 
    915         public long currentTimeMillis() {
    916             return System.currentTimeMillis();
    917         }
    918 
    919         public long elapsedRealtimeNanos() {
    920             return SystemClock.elapsedRealtimeNanos();
    921         }
    922 
    923         public int getUserSerialNumber(UserManager userManager, int userId) {
    924             return userManager.getUserSerialNumber(userId);
    925         }
    926 
    927         public int getUserId(UserManager userManager, int userSerialNumber) {
    928             return userManager.getUserHandle(userSerialNumber);
    929         }
    930 
    931         public int[] getProfileIds(UserManager userManager, int userId) {
    932             if (userManager != null) {
    933                 return userManager.getProfileIds(userId, false);
    934             } else {
    935                 return new int[]{userId};
    936             }
    937         }
    938 
    939         public ActivityManager.StackInfo getFocusedStack() throws RemoteException {
    940             return ActivityManager.getService().getFocusedStackInfo();
    941         }
    942 
    943         public void scheduleIdleJob(Context context) {
    944             BrightnessIdleJob.scheduleJob(context);
    945         }
    946 
    947         public void cancelIdleJob(Context context) {
    948             BrightnessIdleJob.cancelJob(context);
    949         }
    950 
    951         public boolean isInteractive(Context context) {
    952             return context.getSystemService(PowerManager.class).isInteractive();
    953         }
    954     }
    955 }
    956