1 /*
2  * Copyright (C) 2018 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 android.car.cluster;
18 
19 import android.car.Car;
20 import android.car.CarOccupantZoneManager;
21 import android.car.CarOccupantZoneManager.OccupantZoneInfo;
22 import android.content.Context;
23 import android.hardware.display.DisplayManager.DisplayListener;
24 import android.util.Log;
25 import android.view.Display;
26 
27 import com.android.internal.util.Preconditions;
28 
29 import java.util.List;
30 
31 /**
32  * This class provides a display for instrument cluster renderer.
33  * <p>
34  * By default it will try to provide physical secondary display if it is connected, if secondary
35  * display is not connected during creation of this class then it will wait for the display will
36  * be added.
37  */
38 public class ClusterDisplayProvider {
39     private static final String TAG = "Cluster.DisplayProvider";
40     private static final boolean DEBUG = false;
41 
42     private final DisplayListener mListener;
43     private final Car mCar;
44     private CarOccupantZoneManager mOccupantZoneManager;
45 
46     private int mClusterDisplayId = Display.INVALID_DISPLAY;
47 
ClusterDisplayProvider(Context context, DisplayListener clusterDisplayListener)48     ClusterDisplayProvider(Context context, DisplayListener clusterDisplayListener) {
49         mListener = clusterDisplayListener;
50         mCar = Car.createCar(context, null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
51                 (car, ready) -> {
52                     if (!ready) return;
53                     initClusterDisplayProvider(context, (CarOccupantZoneManager) car.getCarManager(
54                             Car.CAR_OCCUPANT_ZONE_SERVICE));
55                 });
56     }
57 
release()58     void release() {
59         if (mCar != null && mCar.isConnected()) {
60             mCar.disconnect();
61         }
62     }
63 
initClusterDisplayProvider( Context context, CarOccupantZoneManager occupantZoneManager)64     private void initClusterDisplayProvider(
65             Context context, CarOccupantZoneManager occupantZoneManager) {
66         Preconditions.checkArgument(
67                 occupantZoneManager != null,"Can't get CarOccupantZoneManager");
68         mOccupantZoneManager = occupantZoneManager;
69         checkClusterDisplayAdded();
70         mOccupantZoneManager.registerOccupantZoneConfigChangeListener(
71                 new ClusterDisplayChangeListener());
72     }
73 
checkClusterDisplayAdded()74     private void checkClusterDisplayAdded() {
75         Display clusterDisplay = getClusterDisplay();
76         if (clusterDisplay != null) {
77             Log.i(TAG, String.format("Found display: %s (id: %d, owner: %s)",
78                     clusterDisplay.getName(), clusterDisplay.getDisplayId(),
79                     clusterDisplay.getOwnerPackageName()));
80             mClusterDisplayId = clusterDisplay.getDisplayId();
81             mListener.onDisplayAdded(clusterDisplay.getDisplayId());
82         }
83     }
84 
getClusterDisplay()85     private Display getClusterDisplay() {
86         List<OccupantZoneInfo> zones = mOccupantZoneManager.getAllOccupantZones();
87         int zones_size = zones.size();
88         for (int i = 0; i < zones_size; ++i) {
89             OccupantZoneInfo zone = zones.get(i);
90             // Assumes that a Car has only one driver.
91             if (zone.occupantType == CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER) {
92                 return mOccupantZoneManager.getDisplayForOccupant(
93                         zone, CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER);
94             }
95         }
96         Log.e(TAG, "Can't find the OccupantZoneInfo for driver");
97         return null;
98     }
99 
100     private final class ClusterDisplayChangeListener implements
101             CarOccupantZoneManager.OccupantZoneConfigChangeListener {
102         @Override
onOccupantZoneConfigChanged(int changeFlags)103         public void onOccupantZoneConfigChanged(int changeFlags) {
104             if (DEBUG) Log.d(TAG, "onOccupantZoneConfigChanged changeFlags=" + changeFlags);
105             if ((changeFlags & CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_DISPLAY) == 0) {
106                 return;
107             }
108             if (mClusterDisplayId == Display.INVALID_DISPLAY) {
109                 checkClusterDisplayAdded();
110             } else {
111                 Display clusterDisplay = getClusterDisplay();
112                 if (clusterDisplay == null) {
113                     mListener.onDisplayRemoved(mClusterDisplayId);
114                     mClusterDisplayId = Display.INVALID_DISPLAY;
115                 }
116             }
117         }
118     }
119 
120     @Override
toString()121     public String toString() {
122         return getClass().getSimpleName() + "{"
123                 + " clusterDisplayId = " + mClusterDisplayId
124                 + "}";
125     }
126 }