DisplayManagerGlobal.java revision 122df868919f3e2b9a92d6e9dc66808884f3f080
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
17package android.hardware.display;
18
19import android.content.Context;
20import android.content.pm.ParceledListSlice;
21import android.content.res.Resources;
22import android.graphics.Point;
23import android.hardware.display.DisplayManager.DisplayListener;
24import android.media.projection.IMediaProjection;
25import android.media.projection.MediaProjection;
26import android.os.Handler;
27import android.os.IBinder;
28import android.os.Looper;
29import android.os.Message;
30import android.os.RemoteException;
31import android.os.ServiceManager;
32import android.text.TextUtils;
33import android.util.Log;
34import android.util.Pair;
35import android.util.SparseArray;
36import android.view.Display;
37import android.view.DisplayAdjustments;
38import android.view.DisplayInfo;
39import android.view.Surface;
40
41import java.util.ArrayList;
42import java.util.Collections;
43import java.util.List;
44
45/**
46 * Manager communication with the display manager service on behalf of
47 * an application process.  You're probably looking for {@link DisplayManager}.
48 *
49 * @hide
50 */
51public final class DisplayManagerGlobal {
52    private static final String TAG = "DisplayManager";
53    private static final boolean DEBUG = false;
54
55    // True if display info and display ids should be cached.
56    //
57    // FIXME: The cache is currently disabled because it's unclear whether we have the
58    // necessary guarantees that the caches will always be flushed before clients
59    // attempt to observe their new state.  For example, depending on the order
60    // in which the binder transactions take place, we might have a problem where
61    // an application could start processing a configuration change due to a display
62    // orientation change before the display info cache has actually been invalidated.
63    private static final boolean USE_CACHE = false;
64
65    public static final int EVENT_DISPLAY_ADDED = 1;
66    public static final int EVENT_DISPLAY_CHANGED = 2;
67    public static final int EVENT_DISPLAY_REMOVED = 3;
68
69    private static DisplayManagerGlobal sInstance;
70
71    private final Object mLock = new Object();
72
73    private final IDisplayManager mDm;
74
75    private DisplayManagerCallback mCallback;
76    private final ArrayList<DisplayListenerDelegate> mDisplayListeners =
77            new ArrayList<DisplayListenerDelegate>();
78
79    private final SparseArray<DisplayInfo> mDisplayInfoCache = new SparseArray<DisplayInfo>();
80    private int[] mDisplayIdCache;
81
82    private int mWifiDisplayScanNestCount;
83
84    private DisplayManagerGlobal(IDisplayManager dm) {
85        mDm = dm;
86    }
87
88    /**
89     * Gets an instance of the display manager global singleton.
90     *
91     * @return The display manager instance, may be null early in system startup
92     * before the display manager has been fully initialized.
93     */
94    public static DisplayManagerGlobal getInstance() {
95        synchronized (DisplayManagerGlobal.class) {
96            if (sInstance == null) {
97                IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE);
98                if (b != null) {
99                    sInstance = new DisplayManagerGlobal(IDisplayManager.Stub.asInterface(b));
100                }
101            }
102            return sInstance;
103        }
104    }
105
106    /**
107     * Get information about a particular logical display.
108     *
109     * @param displayId The logical display id.
110     * @return Information about the specified display, or null if it does not exist.
111     * This object belongs to an internal cache and should be treated as if it were immutable.
112     */
113    public DisplayInfo getDisplayInfo(int displayId) {
114        try {
115            synchronized (mLock) {
116                DisplayInfo info;
117                if (USE_CACHE) {
118                    info = mDisplayInfoCache.get(displayId);
119                    if (info != null) {
120                        return info;
121                    }
122                }
123
124                info = mDm.getDisplayInfo(displayId);
125                if (info == null) {
126                    return null;
127                }
128
129                if (USE_CACHE) {
130                    mDisplayInfoCache.put(displayId, info);
131                }
132                registerCallbackIfNeededLocked();
133
134                if (DEBUG) {
135                    Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info);
136                }
137                return info;
138            }
139        } catch (RemoteException ex) {
140            throw ex.rethrowFromSystemServer();
141        }
142    }
143
144    /**
145     * Gets all currently valid logical display ids.
146     *
147     * @return An array containing all display ids.
148     */
149    public int[] getDisplayIds() {
150        try {
151            synchronized (mLock) {
152                if (USE_CACHE) {
153                    if (mDisplayIdCache != null) {
154                        return mDisplayIdCache;
155                    }
156                }
157
158                int[] displayIds = mDm.getDisplayIds();
159                if (USE_CACHE) {
160                    mDisplayIdCache = displayIds;
161                }
162                registerCallbackIfNeededLocked();
163                return displayIds;
164            }
165        } catch (RemoteException ex) {
166            throw ex.rethrowFromSystemServer();
167        }
168    }
169
170    /**
171     * Gets information about a logical display.
172     *
173     * The display metrics may be adjusted to provide compatibility
174     * for legacy applications or limited screen areas.
175     *
176     * @param displayId The logical display id.
177     * @param daj The compatibility info and activityToken.
178     * @return The display object, or null if there is no display with the given id.
179     */
180    public Display getCompatibleDisplay(int displayId, DisplayAdjustments daj) {
181        DisplayInfo displayInfo = getDisplayInfo(displayId);
182        if (displayInfo == null) {
183            return null;
184        }
185        return new Display(this, displayId, displayInfo, daj);
186    }
187
188    /**
189     * Gets information about a logical display.
190     *
191     * The display metrics may be adjusted to provide compatibility
192     * for legacy applications or limited screen areas.
193     *
194     * @param displayId The logical display id.
195     * @param resources Resources providing compatibility info.
196     * @return The display object, or null if there is no display with the given id.
197     */
198    public Display getCompatibleDisplay(int displayId, Resources resources) {
199        DisplayInfo displayInfo = getDisplayInfo(displayId);
200        if (displayInfo == null) {
201            return null;
202        }
203        return new Display(this, displayId, displayInfo, resources);
204    }
205
206    /**
207     * Gets information about a logical display without applying any compatibility metrics.
208     *
209     * @param displayId The logical display id.
210     * @return The display object, or null if there is no display with the given id.
211     */
212    public Display getRealDisplay(int displayId) {
213        return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
214    }
215
216    public void registerDisplayListener(DisplayListener listener, Handler handler) {
217        if (listener == null) {
218            throw new IllegalArgumentException("listener must not be null");
219        }
220
221        synchronized (mLock) {
222            int index = findDisplayListenerLocked(listener);
223            if (index < 0) {
224                mDisplayListeners.add(new DisplayListenerDelegate(listener, handler));
225                registerCallbackIfNeededLocked();
226            }
227        }
228    }
229
230    public void unregisterDisplayListener(DisplayListener listener) {
231        if (listener == null) {
232            throw new IllegalArgumentException("listener must not be null");
233        }
234
235        synchronized (mLock) {
236            int index = findDisplayListenerLocked(listener);
237            if (index >= 0) {
238                DisplayListenerDelegate d = mDisplayListeners.get(index);
239                d.clearEvents();
240                mDisplayListeners.remove(index);
241            }
242        }
243    }
244
245    private int findDisplayListenerLocked(DisplayListener listener) {
246        final int numListeners = mDisplayListeners.size();
247        for (int i = 0; i < numListeners; i++) {
248            if (mDisplayListeners.get(i).mListener == listener) {
249                return i;
250            }
251        }
252        return -1;
253    }
254
255    private void registerCallbackIfNeededLocked() {
256        if (mCallback == null) {
257            mCallback = new DisplayManagerCallback();
258            try {
259                mDm.registerCallback(mCallback);
260            } catch (RemoteException ex) {
261                throw ex.rethrowFromSystemServer();
262            }
263        }
264    }
265
266    private void handleDisplayEvent(int displayId, int event) {
267        synchronized (mLock) {
268            if (USE_CACHE) {
269                mDisplayInfoCache.remove(displayId);
270
271                if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
272                    mDisplayIdCache = null;
273                }
274            }
275
276            final int numListeners = mDisplayListeners.size();
277            for (int i = 0; i < numListeners; i++) {
278                mDisplayListeners.get(i).sendDisplayEvent(displayId, event);
279            }
280        }
281    }
282
283    public void startWifiDisplayScan() {
284        synchronized (mLock) {
285            if (mWifiDisplayScanNestCount++ == 0) {
286                registerCallbackIfNeededLocked();
287                try {
288                    mDm.startWifiDisplayScan();
289                } catch (RemoteException ex) {
290                    throw ex.rethrowFromSystemServer();
291                }
292            }
293        }
294    }
295
296    public void stopWifiDisplayScan() {
297        synchronized (mLock) {
298            if (--mWifiDisplayScanNestCount == 0) {
299                try {
300                    mDm.stopWifiDisplayScan();
301                } catch (RemoteException ex) {
302                    throw ex.rethrowFromSystemServer();
303                }
304            } else if (mWifiDisplayScanNestCount < 0) {
305                Log.wtf(TAG, "Wifi display scan nest count became negative: "
306                        + mWifiDisplayScanNestCount);
307                mWifiDisplayScanNestCount = 0;
308            }
309        }
310    }
311
312    public void connectWifiDisplay(String deviceAddress) {
313        if (deviceAddress == null) {
314            throw new IllegalArgumentException("deviceAddress must not be null");
315        }
316
317        try {
318            mDm.connectWifiDisplay(deviceAddress);
319        } catch (RemoteException ex) {
320            throw ex.rethrowFromSystemServer();
321        }
322    }
323
324    public void pauseWifiDisplay() {
325        try {
326            mDm.pauseWifiDisplay();
327        } catch (RemoteException ex) {
328            throw ex.rethrowFromSystemServer();
329        }
330    }
331
332    public void resumeWifiDisplay() {
333        try {
334            mDm.resumeWifiDisplay();
335        } catch (RemoteException ex) {
336            throw ex.rethrowFromSystemServer();
337        }
338    }
339
340    public void disconnectWifiDisplay() {
341        try {
342            mDm.disconnectWifiDisplay();
343        } catch (RemoteException ex) {
344            throw ex.rethrowFromSystemServer();
345        }
346    }
347
348    public void renameWifiDisplay(String deviceAddress, String alias) {
349        if (deviceAddress == null) {
350            throw new IllegalArgumentException("deviceAddress must not be null");
351        }
352
353        try {
354            mDm.renameWifiDisplay(deviceAddress, alias);
355        } catch (RemoteException ex) {
356            throw ex.rethrowFromSystemServer();
357        }
358    }
359
360    public void forgetWifiDisplay(String deviceAddress) {
361        if (deviceAddress == null) {
362            throw new IllegalArgumentException("deviceAddress must not be null");
363        }
364
365        try {
366            mDm.forgetWifiDisplay(deviceAddress);
367        } catch (RemoteException ex) {
368            throw ex.rethrowFromSystemServer();
369        }
370    }
371
372    public WifiDisplayStatus getWifiDisplayStatus() {
373        try {
374            return mDm.getWifiDisplayStatus();
375        } catch (RemoteException ex) {
376            throw ex.rethrowFromSystemServer();
377        }
378    }
379
380    public void requestColorMode(int displayId, int colorMode) {
381        try {
382            mDm.requestColorMode(displayId, colorMode);
383        } catch (RemoteException ex) {
384            throw ex.rethrowFromSystemServer();
385        }
386    }
387
388    /**
389     * Set the level of color saturation to apply to the display.
390     */
391    public void setSaturationLevel(float level) {
392        try {
393            mDm.setSaturationLevel(level);
394        } catch (RemoteException ex) {
395            throw ex.rethrowFromSystemServer();
396        }
397    }
398
399    public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection,
400            String name, int width, int height, int densityDpi, Surface surface, int flags,
401            VirtualDisplay.Callback callback, Handler handler, String uniqueId) {
402        if (TextUtils.isEmpty(name)) {
403            throw new IllegalArgumentException("name must be non-null and non-empty");
404        }
405        if (width <= 0 || height <= 0 || densityDpi <= 0) {
406            throw new IllegalArgumentException("width, height, and densityDpi must be "
407                    + "greater than 0");
408        }
409
410        VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler);
411        IMediaProjection projectionToken = projection != null ? projection.getProjection() : null;
412        int displayId;
413        try {
414            displayId = mDm.createVirtualDisplay(callbackWrapper, projectionToken,
415                    context.getPackageName(), name, width, height, densityDpi, surface, flags,
416                    uniqueId);
417        } catch (RemoteException ex) {
418            throw ex.rethrowFromSystemServer();
419        }
420        if (displayId < 0) {
421            Log.e(TAG, "Could not create virtual display: " + name);
422            return null;
423        }
424        Display display = getRealDisplay(displayId);
425        if (display == null) {
426            Log.wtf(TAG, "Could not obtain display info for newly created "
427                    + "virtual display: " + name);
428            try {
429                mDm.releaseVirtualDisplay(callbackWrapper);
430            } catch (RemoteException ex) {
431                throw ex.rethrowFromSystemServer();
432            }
433            return null;
434        }
435        return new VirtualDisplay(this, display, callbackWrapper, surface);
436    }
437
438    public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) {
439        try {
440            mDm.setVirtualDisplaySurface(token, surface);
441        } catch (RemoteException ex) {
442            throw ex.rethrowFromSystemServer();
443        }
444    }
445
446    public void resizeVirtualDisplay(IVirtualDisplayCallback token,
447            int width, int height, int densityDpi) {
448        try {
449            mDm.resizeVirtualDisplay(token, width, height, densityDpi);
450        } catch (RemoteException ex) {
451            throw ex.rethrowFromSystemServer();
452        }
453    }
454
455    public void releaseVirtualDisplay(IVirtualDisplayCallback token) {
456        try {
457            mDm.releaseVirtualDisplay(token);
458        } catch (RemoteException ex) {
459            throw ex.rethrowFromSystemServer();
460        }
461    }
462
463    /**
464     * Gets the stable device display size, in pixels.
465     */
466    public Point getStableDisplaySize() {
467        try {
468            return mDm.getStableDisplaySize();
469        } catch (RemoteException ex) {
470            throw ex.rethrowFromSystemServer();
471        }
472    }
473
474    /**
475     * Retrieves brightness change events.
476     */
477    public List<BrightnessChangeEvent> getBrightnessEvents(String callingPackage) {
478        try {
479            ParceledListSlice<BrightnessChangeEvent> events =
480                    mDm.getBrightnessEvents(callingPackage);
481            if (events == null) {
482                return Collections.emptyList();
483            }
484            return events.getList();
485        } catch (RemoteException ex) {
486            throw ex.rethrowFromSystemServer();
487        }
488    }
489
490    /**
491     * Sets the global brightness configuration for a given user.
492     *
493     * @hide
494     */
495    public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId,
496            String packageName) {
497        try {
498            mDm.setBrightnessConfigurationForUser(c, userId, packageName);
499        } catch (RemoteException ex) {
500            throw ex.rethrowFromSystemServer();
501        }
502    }
503
504    /**
505     * Gets the global brightness configuration for a given user or null if one hasn't been set.
506     *
507     * @hide
508     */
509    public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
510        try {
511            return mDm.getBrightnessConfigurationForUser(userId);
512        } catch (RemoteException ex) {
513            throw ex.rethrowFromSystemServer();
514        }
515    }
516
517    /**
518     * Gets the default brightness configuration or null if one hasn't been configured.
519     *
520     * @hide
521     */
522    public BrightnessConfiguration getDefaultBrightnessConfiguration() {
523        try {
524            return mDm.getDefaultBrightnessConfiguration();
525        } catch (RemoteException ex) {
526            throw ex.rethrowFromSystemServer();
527        }
528    }
529
530    /**
531     * Temporarily sets the brightness of the display.
532     * <p>
533     * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
534     * </p>
535     *
536     * @param brightness The brightness value from 0 to 255.
537     *
538     * @hide Requires signature permission.
539     */
540    public void setTemporaryBrightness(int brightness) {
541        try {
542            mDm.setTemporaryBrightness(brightness);
543        } catch (RemoteException ex) {
544            throw ex.rethrowFromSystemServer();
545        }
546    }
547
548    /**
549     * Temporarily sets the auto brightness adjustment factor.
550     * <p>
551     * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
552     * </p>
553     *
554     * @param adjustment The adjustment factor from -1.0 to 1.0.
555     *
556     * @hide Requires signature permission.
557     */
558    public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
559        try {
560            mDm.setTemporaryAutoBrightnessAdjustment(adjustment);
561        } catch (RemoteException ex) {
562            throw ex.rethrowFromSystemServer();
563        }
564    }
565
566    /**
567     * Returns the minimum brightness curve, which guarantess that any brightness curve that dips
568     * below it is rejected by the system.
569     * This prevent auto-brightness from setting the screen so dark as to prevent the user from
570     * resetting or disabling it, and maps lux to the absolute minimum nits that are still readable
571     * in that ambient brightness.
572     *
573     * @return The minimum brightness curve (as lux values and their corresponding nits values).
574     */
575    public Pair<float[], float[]> getMinimumBrightnessCurve() {
576        try {
577            Curve curve = mDm.getMinimumBrightnessCurve();
578            return Pair.create(curve.getX(), curve.getY());
579        } catch (RemoteException ex) {
580            throw ex.rethrowFromSystemServer();
581        }
582    }
583
584    /**
585     * Retrieves ambient brightness stats.
586     */
587    public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
588        try {
589            ParceledListSlice<AmbientBrightnessDayStats> stats = mDm.getAmbientBrightnessStats();
590            if (stats == null) {
591                return Collections.emptyList();
592            }
593            return stats.getList();
594        } catch (RemoteException ex) {
595            throw ex.rethrowFromSystemServer();
596        }
597    }
598
599    private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
600        @Override
601        public void onDisplayEvent(int displayId, int event) {
602            if (DEBUG) {
603                Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event);
604            }
605            handleDisplayEvent(displayId, event);
606        }
607    }
608
609    private static final class DisplayListenerDelegate extends Handler {
610        public final DisplayListener mListener;
611
612        public DisplayListenerDelegate(DisplayListener listener, Handler handler) {
613            super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
614            mListener = listener;
615        }
616
617        public void sendDisplayEvent(int displayId, int event) {
618            Message msg = obtainMessage(event, displayId, 0);
619            sendMessage(msg);
620        }
621
622        public void clearEvents() {
623            removeCallbacksAndMessages(null);
624        }
625
626        @Override
627        public void handleMessage(Message msg) {
628            switch (msg.what) {
629                case EVENT_DISPLAY_ADDED:
630                    mListener.onDisplayAdded(msg.arg1);
631                    break;
632                case EVENT_DISPLAY_CHANGED:
633                    mListener.onDisplayChanged(msg.arg1);
634                    break;
635                case EVENT_DISPLAY_REMOVED:
636                    mListener.onDisplayRemoved(msg.arg1);
637                    break;
638            }
639        }
640    }
641
642    private final static class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub {
643        private VirtualDisplayCallbackDelegate mDelegate;
644
645        public VirtualDisplayCallback(VirtualDisplay.Callback callback, Handler handler) {
646            if (callback != null) {
647                mDelegate = new VirtualDisplayCallbackDelegate(callback, handler);
648            }
649        }
650
651        @Override // Binder call
652        public void onPaused() {
653            if (mDelegate != null) {
654                mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_PAUSED);
655            }
656        }
657
658        @Override // Binder call
659        public void onResumed() {
660            if (mDelegate != null) {
661                mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_RESUMED);
662            }
663        }
664
665        @Override // Binder call
666        public void onStopped() {
667            if (mDelegate != null) {
668                mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_STOPPED);
669            }
670        }
671    }
672
673    private final static class VirtualDisplayCallbackDelegate extends Handler {
674        public static final int MSG_DISPLAY_PAUSED = 0;
675        public static final int MSG_DISPLAY_RESUMED = 1;
676        public static final int MSG_DISPLAY_STOPPED = 2;
677
678        private final VirtualDisplay.Callback mCallback;
679
680        public VirtualDisplayCallbackDelegate(VirtualDisplay.Callback callback,
681                Handler handler) {
682            super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
683            mCallback = callback;
684        }
685
686        @Override
687        public void handleMessage(Message msg) {
688            switch (msg.what) {
689                case MSG_DISPLAY_PAUSED:
690                    mCallback.onPaused();
691                    break;
692                case MSG_DISPLAY_RESUMED:
693                    mCallback.onResumed();
694                    break;
695                case MSG_DISPLAY_STOPPED:
696                    mCallback.onStopped();
697                    break;
698            }
699        }
700    }
701}
702