blob: 361d92843de961107c8a9014a00e71eeb2d726f6 [file] [log] [blame]
Kenny Guy22bd0442017-10-26 00:15:54 +01001/*
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
17package com.android.server.display;
18
19import android.annotation.Nullable;
20import android.app.ActivityManager;
21import android.content.BroadcastReceiver;
22import android.content.ContentResolver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.pm.ParceledListSlice;
27import android.database.ContentObserver;
28import android.hardware.Sensor;
29import android.hardware.SensorEvent;
30import android.hardware.SensorEventListener;
31import android.hardware.SensorManager;
32import android.hardware.display.BrightnessChangeEvent;
33import android.net.Uri;
34import android.os.BatteryManager;
35import android.os.Environment;
36import android.os.Handler;
37import android.os.RemoteException;
38import android.os.SystemClock;
39import android.os.UserHandle;
40import android.os.UserManager;
41import android.provider.Settings;
42import android.util.AtomicFile;
43import android.util.Slog;
44import android.util.Xml;
45
46import com.android.internal.annotations.GuardedBy;
47import com.android.internal.annotations.VisibleForTesting;
48import com.android.internal.os.BackgroundThread;
49import com.android.internal.util.FastXmlSerializer;
50import com.android.internal.util.RingBuffer;
51
52import libcore.io.IoUtils;
53
54import org.xmlpull.v1.XmlPullParser;
55import org.xmlpull.v1.XmlPullParserException;
56import org.xmlpull.v1.XmlSerializer;
57
58import java.io.File;
59import java.io.FileInputStream;
60import java.io.FileOutputStream;
61import java.io.IOException;
62import java.io.InputStream;
63import java.io.OutputStream;
64import java.nio.charset.StandardCharsets;
65import java.util.ArrayDeque;
66import java.util.ArrayList;
67
68import java.util.Deque;
69import java.util.List;
70import java.util.concurrent.TimeUnit;
71
72/**
73 * Class that tracks recent brightness settings changes and stores
74 * associated information such as light sensor readings.
75 */
76public class BrightnessTracker {
77
78 private static final String TAG = "BrightnessTracker";
79 private static final boolean DEBUG = false;
80
81 private static final String EVENTS_FILE = "brightness_events.xml";
82 private static final int MAX_EVENTS = 100;
83 // Discard events when reading or writing that are older than this.
84 private static final long MAX_EVENT_AGE = TimeUnit.DAYS.toMillis(30);
85 // Time over which we keep lux sensor readings.
86 private static final long LUX_EVENT_HORIZON = TimeUnit.SECONDS.toNanos(10);
87
88 private static final String TAG_EVENTS = "events";
89 private static final String TAG_EVENT = "event";
90 private static final String ATTR_BRIGHTNESS = "brightness";
91 private static final String ATTR_TIMESTAMP = "timestamp";
92 private static final String ATTR_PACKAGE_NAME = "packageName";
93 private static final String ATTR_USER = "user";
94 private static final String ATTR_LUX = "lux";
95 private static final String ATTR_LUX_TIMESTAMPS = "luxTimestamps";
96 private static final String ATTR_BATTERY_LEVEL = "batteryLevel";
97 private static final String ATTR_NIGHT_MODE = "nightMode";
98 private static final String ATTR_COLOR_TEMPERATURE = "colorTemperature";
99 private static final String ATTR_LAST_BRIGHTNESS = "lastBrightness";
100
101 // Lock held while accessing mEvents, is held while writing events to flash.
102 private final Object mEventsLock = new Object();
103 @GuardedBy("mEventsLock")
104 private RingBuffer<BrightnessChangeEvent> mEvents
105 = new RingBuffer<>(BrightnessChangeEvent.class, MAX_EVENTS);
106 private final Runnable mEventsWriter = () -> writeEvents();
107 private volatile boolean mWriteEventsScheduled;
108
109 private UserManager mUserManager;
110 private final Context mContext;
111 private final ContentResolver mContentResolver;
112 private Handler mBgHandler;
113 // mSettingsObserver, mBroadcastReceiver and mSensorListener should only be used on
114 // the mBgHandler thread.
115 private SettingsObserver mSettingsObserver;
116 private BroadcastReceiver mBroadcastReceiver;
117 private SensorListener mSensorListener;
118
119 // Lock held while collecting data related to brightness changes.
120 private final Object mDataCollectionLock = new Object();
121 @GuardedBy("mDataCollectionLock")
122 private Deque<LightData> mLastSensorReadings = new ArrayDeque<>();
123 @GuardedBy("mDataCollectionLock")
124 private float mLastBatteryLevel = Float.NaN;
125 @GuardedBy("mDataCollectionLock")
126 private int mIgnoreBrightness = -1;
127 @GuardedBy("mDataCollectionLock")
128 private int mLastBrightness = -1;
129
130 private final Injector mInjector;
131
132 public BrightnessTracker(Context context, @Nullable Injector injector) {
133 // Note this will be called very early in boot, other system
134 // services may not be present.
135 mContext = context;
136 mContentResolver = context.getContentResolver();
137 if (injector != null) {
138 mInjector = injector;
139 } else {
140 mInjector = new Injector();
141 }
142 }
143
144 /** Start listening for brightness slider events */
145 public void start() {
146 if (DEBUG) {
147 Slog.d(TAG, "Start");
148 }
149 mBgHandler = mInjector.getBackgroundHandler();
150 mUserManager = mContext.getSystemService(UserManager.class);
151
152 mBgHandler.post(() -> backgroundStart());
153 }
154
155 private void backgroundStart() {
156 readEvents();
157
158 mLastBrightness = mInjector.getSystemIntForUser(mContentResolver,
159 Settings.System.SCREEN_BRIGHTNESS, -1,
160 UserHandle.USER_CURRENT);
161
162 mSensorListener = new SensorListener();
163 mInjector.registerSensorListener(mContext, mSensorListener);
164
165 mSettingsObserver = new SettingsObserver(mBgHandler);
166 mInjector.registerBrightnessObserver(mContentResolver, mSettingsObserver);
167
168 final IntentFilter intentFilter = new IntentFilter();
169 intentFilter.addAction(Intent.ACTION_SHUTDOWN);
170 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
171 mBroadcastReceiver = new Receiver();
172 mInjector.registerReceiver(mContext, mBroadcastReceiver, intentFilter);
173 }
174
175 /** Stop listening for events */
176 @VisibleForTesting
177 void stop() {
178 if (DEBUG) {
179 Slog.d(TAG, "Stop");
180 }
181 mInjector.unregisterSensorListener(mContext, mSensorListener);
182 mInjector.unregisterReceiver(mContext, mBroadcastReceiver);
183 mInjector.unregisterBrightnessObserver(mContext, mSettingsObserver);
184 }
185
186 /**
187 * @param userId userId to fetch data for.
188 * @return List of recent {@link BrightnessChangeEvent}s
189 */
190 public ParceledListSlice<BrightnessChangeEvent> getEvents(int userId) {
191 // TODO include apps from any managed profiles in the brightness information.
192 BrightnessChangeEvent[] events;
193 synchronized (mEventsLock) {
194 events = mEvents.toArray();
195 }
196 ArrayList<BrightnessChangeEvent> out = new ArrayList<>(events.length);
197 for (int i = 0; i < events.length; ++i) {
198 if (events[i].userId == userId) {
199 out.add(events[i]);
200 }
201 }
202 return new ParceledListSlice<>(out);
203 }
204
205 /** Sets brightness without logging the brightness change event */
206 public void setBrightness(int brightness, int userId) {
207 synchronized (mDataCollectionLock) {
208 mIgnoreBrightness = brightness;
209 }
210 mInjector.putSystemIntForUser(mContentResolver, Settings.System.SCREEN_BRIGHTNESS,
211 brightness, userId);
212 }
213
214 private void handleBrightnessChanged() {
215 if (DEBUG) {
216 Slog.d(TAG, "Brightness change");
217 }
218 final BrightnessChangeEvent event = new BrightnessChangeEvent();
219 event.timeStamp = mInjector.currentTimeMillis();
220
221 int brightness = mInjector.getSystemIntForUser(mContentResolver,
222 Settings.System.SCREEN_BRIGHTNESS, -1,
223 UserHandle.USER_CURRENT);
224
225 synchronized (mDataCollectionLock) {
226 int previousBrightness = mLastBrightness;
227 mLastBrightness = brightness;
228
229 if (brightness == -1 || brightness == mIgnoreBrightness) {
230 // Notified of brightness change but no setting or self change so ignore.
231 mIgnoreBrightness = -1;
232 return;
233 }
234
235 final int readingCount = mLastSensorReadings.size();
236 if (readingCount == 0) {
237 // No sensor data so ignore this.
238 return;
239 }
240
241 event.luxValues = new float[readingCount];
242 event.luxTimestamps = new long[readingCount];
243
244 int pos = 0;
245
246 // Convert sensor timestamp in elapsed time nanos to current time millis.
247 long currentTimeMillis = mInjector.currentTimeMillis();
248 long elapsedTimeNanos = mInjector.elapsedRealtimeNanos();
249 for (LightData reading : mLastSensorReadings) {
250 event.luxValues[pos] = reading.lux;
251 event.luxTimestamps[pos] = currentTimeMillis -
252 TimeUnit.NANOSECONDS.toMillis(elapsedTimeNanos - reading.timestamp);
253 ++pos;
254 }
255
256 event.batteryLevel = mLastBatteryLevel;
257 event.lastBrightness = previousBrightness;
258 }
259
260 event.brightness = brightness;
261
262 try {
263 final ActivityManager.StackInfo focusedStack = mInjector.getFocusedStack();
264 event.userId = focusedStack.userId;
265 event.packageName = focusedStack.topActivity.getPackageName();
266 } catch (RemoteException e) {
267 // Really shouldn't be possible.
268 }
269
270 event.nightMode = mInjector.getSecureIntForUser(mContentResolver,
271 Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 0, UserHandle.USER_CURRENT)
272 == 1;
273 event.colorTemperature = mInjector.getSecureIntForUser(mContentResolver,
274 Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
275 0, UserHandle.USER_CURRENT);
276
277 if (DEBUG) {
278 Slog.d(TAG, "Event " + event.brightness + " " + event.packageName);
279 }
280 synchronized (mEventsLock) {
281 mEvents.append(event);
282 }
283 }
284
285 private void scheduleWriteEvents() {
286 if (!mWriteEventsScheduled) {
287 mBgHandler.post(mEventsWriter);
288 mWriteEventsScheduled = true;
289 }
290 }
291
292 private void writeEvents() {
293 mWriteEventsScheduled = false;
294 // TODO kick off write on handler thread e.g. every 24 hours.
295 synchronized (mEventsLock) {
296 final AtomicFile writeTo = mInjector.getFile();
297 if (writeTo == null) {
298 return;
299 }
300 if (mEvents.isEmpty()) {
301 if (writeTo.exists()) {
302 writeTo.delete();
303 }
304 } else {
305 FileOutputStream output = null;
306 try {
307 output = writeTo.startWrite();
308 writeEventsLocked(output);
309 writeTo.finishWrite(output);
310 } catch (IOException e) {
311 writeTo.failWrite(output);
312 Slog.e(TAG, "Failed to write change mEvents.", e);
313 }
314 }
315 }
316 }
317
318 private void readEvents() {
319 synchronized (mEventsLock) {
320 mEvents.clear();
321 final AtomicFile readFrom = mInjector.getFile();
322 if (readFrom != null && readFrom.exists()) {
323 FileInputStream input = null;
324 try {
325 input = readFrom.openRead();
326 readEventsLocked(input);
327 } catch (IOException e) {
328 readFrom.delete();
329 Slog.e(TAG, "Failed to read change mEvents.", e);
330 } finally {
331 IoUtils.closeQuietly(input);
332 }
333 }
334 }
335 }
336
337 @VisibleForTesting
338 @GuardedBy("mEventsLock")
339 void writeEventsLocked(OutputStream stream) throws IOException {
340 XmlSerializer out = new FastXmlSerializer();
341 out.setOutput(stream, StandardCharsets.UTF_8.name());
342 out.startDocument(null, true);
343 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
344
345 out.startTag(null, TAG_EVENTS);
346 BrightnessChangeEvent[] toWrite = mEvents.toArray();
347 if (DEBUG) {
348 Slog.d(TAG, "Writing events " + toWrite.length);
349 }
350 final long timeCutOff = System.currentTimeMillis() - MAX_EVENT_AGE;
351 for (int i = 0; i < toWrite.length; ++i) {
352 int userSerialNo = mInjector.getUserSerialNumber(mUserManager, toWrite[i].userId);
353 if (userSerialNo != -1 && toWrite[i].timeStamp > timeCutOff) {
354 out.startTag(null, TAG_EVENT);
355 out.attribute(null, ATTR_BRIGHTNESS, Integer.toString(toWrite[i].brightness));
356 out.attribute(null, ATTR_TIMESTAMP, Long.toString(toWrite[i].timeStamp));
357 out.attribute(null, ATTR_PACKAGE_NAME, toWrite[i].packageName);
358 out.attribute(null, ATTR_USER, Integer.toString(userSerialNo));
359 out.attribute(null, ATTR_BATTERY_LEVEL, Float.toString(toWrite[i].batteryLevel));
360 out.attribute(null, ATTR_NIGHT_MODE, Boolean.toString(toWrite[i].nightMode));
361 out.attribute(null, ATTR_COLOR_TEMPERATURE, Integer.toString(
362 toWrite[i].colorTemperature));
363 out.attribute(null, ATTR_LAST_BRIGHTNESS,
364 Integer.toString(toWrite[i].lastBrightness));
365 StringBuilder luxValues = new StringBuilder();
366 StringBuilder luxTimestamps = new StringBuilder();
367 for (int j = 0; j < toWrite[i].luxValues.length; ++j) {
368 if (j > 0) {
369 luxValues.append(',');
370 luxTimestamps.append(',');
371 }
372 luxValues.append(Float.toString(toWrite[i].luxValues[j]));
373 luxTimestamps.append(Long.toString(toWrite[i].luxTimestamps[j]));
374 }
375 out.attribute(null, ATTR_LUX, luxValues.toString());
376 out.attribute(null, ATTR_LUX_TIMESTAMPS, luxTimestamps.toString());
377 out.endTag(null, TAG_EVENT);
378 }
379 }
380 out.endTag(null, TAG_EVENTS);
381 out.endDocument();
382 stream.flush();
383 }
384
385 @VisibleForTesting
386 @GuardedBy("mEventsLock")
387 void readEventsLocked(InputStream stream) throws IOException {
388 try {
389 XmlPullParser parser = Xml.newPullParser();
390 parser.setInput(stream, StandardCharsets.UTF_8.name());
391
392 int type;
393 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
394 && type != XmlPullParser.START_TAG) {
395 }
396 String tag = parser.getName();
397 if (!TAG_EVENTS.equals(tag)) {
398 throw new XmlPullParserException(
399 "Events not found in brightness tracker file " + tag);
400 }
401
402 final long timeCutOff = mInjector.currentTimeMillis() - MAX_EVENT_AGE;
403
404 parser.next();
405 int outerDepth = parser.getDepth();
406 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
407 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
408 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
409 continue;
410 }
411 tag = parser.getName();
412 if (TAG_EVENT.equals(tag)) {
413 BrightnessChangeEvent event = new BrightnessChangeEvent();
414
415 String brightness = parser.getAttributeValue(null, ATTR_BRIGHTNESS);
416 event.brightness = Integer.parseInt(brightness);
417 String timestamp = parser.getAttributeValue(null, ATTR_TIMESTAMP);
418 event.timeStamp = Long.parseLong(timestamp);
419 event.packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
420 String user = parser.getAttributeValue(null, ATTR_USER);
421 event.userId = mInjector.getUserId(mUserManager, Integer.parseInt(user));
422 String batteryLevel = parser.getAttributeValue(null, ATTR_BATTERY_LEVEL);
423 event.batteryLevel = Float.parseFloat(batteryLevel);
424 String nightMode = parser.getAttributeValue(null, ATTR_NIGHT_MODE);
425 event.nightMode = Boolean.parseBoolean(nightMode);
426 String colorTemperature =
427 parser.getAttributeValue(null, ATTR_COLOR_TEMPERATURE);
428 event.colorTemperature = Integer.parseInt(colorTemperature);
429 String lastBrightness = parser.getAttributeValue(null, ATTR_LAST_BRIGHTNESS);
430 event.lastBrightness = Integer.parseInt(lastBrightness);
431
432 String luxValue = parser.getAttributeValue(null, ATTR_LUX);
433 String luxTimestamp = parser.getAttributeValue(null, ATTR_LUX_TIMESTAMPS);
434
435 String[] luxValues = luxValue.split(",");
436 String[] luxTimestamps = luxTimestamp.split(",");
437 if (luxValues.length != luxTimestamps.length) {
438 continue;
439 }
440 event.luxValues = new float[luxValues.length];
441 event.luxTimestamps = new long[luxValues.length];
442 for (int i = 0; i < luxValues.length; ++i) {
443 event.luxValues[i] = Float.parseFloat(luxValues[i]);
444 event.luxTimestamps[i] = Long.parseLong(luxTimestamps[i]);
445 }
446
447 if (DEBUG) {
448 Slog.i(TAG, "Read event " + event.brightness
449 + " " + event.packageName);
450 }
451
452 if (event.userId != -1 && event.timeStamp > timeCutOff
453 && event.luxValues.length > 0) {
454 mEvents.append(event);
455 }
456 }
457 }
458 } catch (NullPointerException | NumberFormatException | XmlPullParserException
459 | IOException e) {
460 // Failed to parse something, just start with an empty event log.
461 mEvents = new RingBuffer<>(BrightnessChangeEvent.class, MAX_EVENTS);
462 Slog.e(TAG, "Failed to parse brightness event", e);
463 // Re-throw so we will delete the bad file.
464 throw new IOException("failed to parse file", e);
465 }
466 }
467
468 // Not allowed to keep the SensorEvent so used to copy the data we care about.
469 private static class LightData {
470 public float lux;
471 // Time in elapsedRealtimeNanos
472 public long timestamp;
473 }
474
475 private void recordSensorEvent(SensorEvent event) {
476 long horizon = mInjector.elapsedRealtimeNanos() - LUX_EVENT_HORIZON;
477 synchronized (mDataCollectionLock) {
478 if (DEBUG) {
479 Slog.v(TAG, "Sensor event " + event);
480 }
481 if (!mLastSensorReadings.isEmpty()
482 && event.timestamp < mLastSensorReadings.getLast().timestamp) {
483 // Ignore event that came out of order.
484 return;
485 }
486 LightData data = null;
487 while (!mLastSensorReadings.isEmpty()
488 && mLastSensorReadings.getFirst().timestamp < horizon) {
489 // Remove data that has fallen out of the window.
490 data = mLastSensorReadings.removeFirst();
491 }
492 // We put back the last one we removed so we know how long
493 // the first sensor reading was valid for.
494 if (data != null) {
495 mLastSensorReadings.addFirst(data);
496 }
497
498 data = new LightData();
499 data.timestamp = event.timestamp;
500 data.lux = event.values[0];
501 mLastSensorReadings.addLast(data);
502 }
503 }
504
505 private void batteryLevelChanged(int level, int scale) {
506 synchronized (mDataCollectionLock) {
507 mLastBatteryLevel = (float) level / (float) scale;
508 }
509 }
510
511 private final class SensorListener implements SensorEventListener {
512 @Override
513 public void onSensorChanged(SensorEvent event) {
514 recordSensorEvent(event);
515 }
516
517 @Override
518 public void onAccuracyChanged(Sensor sensor, int accuracy) {
519
520 }
521 }
522
523 private final class SettingsObserver extends ContentObserver {
524 public SettingsObserver(Handler handler) {
525 super(handler);
526 }
527
528 @Override
529 public void onChange(boolean selfChange, Uri uri) {
530 if (DEBUG) {
531 Slog.v(TAG, "settings change " + uri);
532 }
533 // Self change is based on observer passed to notifyObserver, SettingsProvider
534 // passes null so no changes are self changes.
535 handleBrightnessChanged();
536 }
537 }
538
539 private final class Receiver extends BroadcastReceiver {
540 @Override
541 public void onReceive(Context context, Intent intent) {
542 if (DEBUG) {
543 Slog.d(TAG, "Received " + intent.getAction());
544 }
545 String action = intent.getAction();
546 if (Intent.ACTION_SHUTDOWN.equals(action)) {
547 stop();
548 scheduleWriteEvents();
549 } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
550 int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
551 int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
552 if (level != -1 && scale != 0) {
553 batteryLevelChanged(level, scale);
554 }
555 }
556 }
557 }
558
559 @VisibleForTesting
560 static class Injector {
561 public void registerSensorListener(Context context,
562 SensorEventListener sensorListener) {
563 SensorManager sensorManager = context.getSystemService(SensorManager.class);
564 Sensor lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
565 sensorManager.registerListener(sensorListener,
566 lightSensor, SensorManager.SENSOR_DELAY_NORMAL);
567 }
568
569 public void unregisterSensorListener(Context context, SensorEventListener sensorListener) {
570 SensorManager sensorManager = context.getSystemService(SensorManager.class);
571 sensorManager.unregisterListener(sensorListener);
572 }
573
574 public void registerBrightnessObserver(ContentResolver resolver,
575 ContentObserver settingsObserver) {
576 resolver.registerContentObserver(Settings.System.getUriFor(
577 Settings.System.SCREEN_BRIGHTNESS),
578 false, settingsObserver, UserHandle.USER_ALL);
579 }
580
581 public void unregisterBrightnessObserver(Context context,
582 ContentObserver settingsObserver) {
583 context.getContentResolver().unregisterContentObserver(settingsObserver);
584 }
585
586 public void registerReceiver(Context context,
587 BroadcastReceiver receiver, IntentFilter filter) {
588 context.registerReceiver(receiver, filter);
589 }
590
591 public void unregisterReceiver(Context context,
592 BroadcastReceiver receiver) {
593 context.unregisterReceiver(receiver);
594 }
595
596 public Handler getBackgroundHandler() {
597 return BackgroundThread.getHandler();
598 }
599
600 public int getSystemIntForUser(ContentResolver resolver, String setting, int defaultValue,
601 int userId) {
602 return Settings.System.getIntForUser(resolver, setting, defaultValue, userId);
603 }
604
605 public void putSystemIntForUser(ContentResolver resolver, String setting, int value,
606 int userId) {
607 Settings.System.putIntForUser(resolver, setting, value, userId);
608 }
609
610 public int getSecureIntForUser(ContentResolver resolver, String setting, int defaultValue,
611 int userId) {
612 return Settings.Secure.getIntForUser(resolver, setting, defaultValue, userId);
613 }
614
615 public AtomicFile getFile() {
616 return new AtomicFile(new File(Environment.getDataSystemDeDirectory(), EVENTS_FILE));
617 }
618
619 public long currentTimeMillis() {
620 return System.currentTimeMillis();
621 }
622
623 public long elapsedRealtimeNanos() {
624 return SystemClock.elapsedRealtimeNanos();
625 }
626
627 public int getUserSerialNumber(UserManager userManager, int userId) {
628 return userManager.getUserSerialNumber(userId);
629 }
630
631 public int getUserId(UserManager userManager, int userSerialNumber) {
632 return userManager.getUserHandle(userSerialNumber);
633 }
634
635 public ActivityManager.StackInfo getFocusedStack() throws RemoteException {
636 return ActivityManager.getService().getFocusedStackInfo();
637 }
638 }
639}