1 /*
2  * Copyright (C) 2022 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.car.caruiportrait.common.service;
18 
19 import android.app.Service;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.os.Bundle;
25 import android.os.Handler;
26 import android.os.IBinder;
27 import android.os.Message;
28 import android.os.Messenger;
29 import android.os.RemoteException;
30 import android.util.Log;
31 
32 import java.util.ArrayList;
33 
34 /**
35  * This application service uses the {@link Messenger} class for communicating with clients
36  * {@link CarUiPortraitLauncher}.  This allows for remote interaction with a service, without
37  * needing to define an AIDL interface.
38  */
39 public class CarUiPortraitService extends Service {
40     public static final String TAG = "CarUiPortraitService";
41 
42     // action name for the intent when requested from system UI
43     public static final String REQUEST_FROM_SYSTEM_UI = "REQUEST_FROM_SYSTEM_UI";
44 
45     // key name for the intent's extra that tells the root task view's visibility status
46     public static final String INTENT_EXTRA_IS_IMMERSIVE_MODE_REQUESTED =
47             "INTENT_EXTRA_IS_IMMERSIVE_MODE_REQUESTED";
48 
49     // key name for the intent's extra that tells the component that request the view's
50     // visibility change.
51     public static final String INTENT_EXTRA_IMMERSIVE_MODE_REQUESTED_SOURCE =
52             "INTENT_EXTRA_IMMERSIVE_MODE_REQUESTED_SOURCE";
53 
54     public static final String INTENT_EXTRA_IS_IMMERSIVE_MODE_STATE =
55             "INTENT_EXTRA_IS_IMMERSIVE_MODE_STATE";
56 
57     // action name for the intent when requested from CarUiPortraitLauncher
58     public static final String REQUEST_FROM_LAUNCHER = "REQUEST_FROM_LAUNCHER";
59 
60     // key name for the intent's extra that tells the system bars visibility status
61     public static final String INTENT_EXTRA_HIDE_SYSTEM_BAR_FOR_IMMERSIVE_MODE =
62             "INTENT_EXTRA_HIDE_SYSTEM_BAR_FOR_IMMERSIVE_MODE";
63 
64     // key name for the intent's extra that tells the app grid's visibility status
65     public static final String INTENT_EXTRA_APP_GRID_VISIBILITY_CHANGE =
66             "INTENT_EXTRA_APP_GRID_VISIBILITY_CHANGE";
67 
68     // key name for the intent's extra that tells the notification's visibility status
69     public static final String INTENT_EXTRA_NOTIFICATION_VISIBILITY_CHANGE =
70             "INTENT_EXTRA_NOTIFICATION_VISIBILITY_CHANGE";
71 
72     // key name for the intent's extra that tells the Recents' visibility status
73     public static final String INTENT_EXTRA_RECENTS_VISIBILITY_CHANGE =
74             "INTENT_EXTRA_RECENTS_VISIBILITY_CHANGE";
75 
76     // key name for the intent's extra that tells if suw is in progress
77     public static final String INTENT_EXTRA_SUW_IN_PROGRESS =
78             "INTENT_EXTRA_SUW_IN_PROGRESS";
79 
80     // key name for the intent's extra that tells if task views are ready
81     public static final String INTENT_EXTRA_FG_TASK_VIEW_READY =
82             "INTENT_EXTRA_TASK_VIEW_READY";
83 
84     // key name for the intent's extra that tells if launcher is ready
85     public static final String INTENT_EXTRA_LAUNCHER_READY =
86             "INTENT_EXTRA_LAUNCHER_READY";
87 
88     // key name for the intent's extra that tells if application panel should be collapsed.
89     public static final String INTENT_EXTRA_COLLAPSE_APPLICATION_PANEL =
90             "INTENT_EXTRA_COLLAPSE_APPLICATION_PANEL";
91 
92     // Keeps track of all current registered clients.
93     private final ArrayList<Messenger> mClients = new ArrayList<Messenger>();
94 
95     private final Messenger mMessenger = new Messenger(new IncomingHandler());
96 
97     /**
98      * Command to the service to register a client, receiving callbacks
99      * from the service. The Message's replyTo field must be a Messenger of
100      * the client where callbacks should be sent.
101      */
102     public static final int MSG_REGISTER_CLIENT = 1;
103 
104     /**
105      * Command to the service to unregister a client, it stop receiving callbacks
106      * from the service. The Message's replyTo field must be a Messenger of
107      * the client as previously given with MSG_REGISTER_CLIENT.
108      */
109     public static final int MSG_UNREGISTER_CLIENT = 2;
110 
111     /**
112      * Command to service to set a new value for app grid visibility.
113      */
114     public static final int MSG_APP_GRID_VISIBILITY_CHANGE = 3;
115 
116     /**
117      * Command to service to set a new value when immersive mode is requested or exited.
118      */
119     public static final int MSG_IMMERSIVE_MODE_REQUESTED = 4;
120 
121     /**
122      * Command to service to set a new value when SUW mode is entered or exited.
123      */
124     public static final int MSG_SUW_IN_PROGRESS = 5;
125 
126     /**
127      * Command to service to set a new value when launcher request to hide the systembars.
128      */
129     public static final int MSG_HIDE_SYSTEM_BAR_FOR_IMMERSIVE = 6;
130 
131     /**
132      * Command to service to notify when task views are ready.
133      */
134     public static final int MSG_FG_TASK_VIEW_READY = 7;
135 
136     /**
137      * Command to service to notify when immersive mode changes
138      */
139     public static final int MSG_IMMERSIVE_MODE_CHANGE = 8;
140 
141     /**
142      * Command to service to notify when SysUI is ready and started.
143      */
144     public static final int MSG_SYSUI_STARTED = 9;
145 
146     /**
147      * Command to service to set a new value for notifications visibility.
148      */
149     public static final int MSG_NOTIFICATIONS_VISIBILITY_CHANGE = 10;
150 
151     /**
152      * Command to service to set a new value for Recents visibility.
153      */
154     public static final int MSG_RECENTS_VISIBILITY_CHANGE = 11;
155 
156     /**
157      * Command to service to collapse notification panel if open.
158      */
159     public static final int MSG_COLLAPSE_APPLICATION = 12;
160 
161     private boolean mIsSystemInImmersiveMode;
162     private String mImmersiveModeSource;
163     private boolean mIsSuwInProgress;
164     private BroadcastReceiver mSysUiRequestsReceiver;
165 
166     /**
167      * Handler of incoming messages from CarUiPortraitLauncher.
168      */
169     class IncomingHandler extends Handler {
170         @Override
handleMessage(Message msg)171         public void handleMessage(Message msg) {
172             Log.d(TAG, "Received message: " + msg.what);
173             switch (msg.what) {
174                 case MSG_SYSUI_STARTED:
175                     // value is passed as 0 because launcher just needs a event and no need for val
176                     notifyClients(MSG_SYSUI_STARTED, 0);
177                     break;
178                 case MSG_REGISTER_CLIENT:
179                     mClients.add(msg.replyTo);
180                     break;
181                 case MSG_UNREGISTER_CLIENT:
182                     mClients.remove(msg.replyTo);
183                     break;
184                 case MSG_APP_GRID_VISIBILITY_CHANGE:
185                     Intent intent = new Intent(REQUEST_FROM_LAUNCHER);
186                     intent.putExtra(INTENT_EXTRA_APP_GRID_VISIBILITY_CHANGE,
187                             intToBoolean(msg.arg1));
188                     CarUiPortraitService.this.sendBroadcast(intent);
189                     break;
190                 case MSG_NOTIFICATIONS_VISIBILITY_CHANGE:
191                     Intent notificationIntent = new Intent(REQUEST_FROM_LAUNCHER);
192                     notificationIntent.putExtra(INTENT_EXTRA_NOTIFICATION_VISIBILITY_CHANGE,
193                             intToBoolean(msg.arg1));
194                     CarUiPortraitService.this.sendBroadcast(notificationIntent);
195                     break;
196                 case MSG_RECENTS_VISIBILITY_CHANGE:
197                     Intent recentsIntent = new Intent(REQUEST_FROM_LAUNCHER);
198                     recentsIntent.putExtra(INTENT_EXTRA_RECENTS_VISIBILITY_CHANGE,
199                             intToBoolean(msg.arg1));
200                     CarUiPortraitService.this.sendBroadcast(recentsIntent);
201                     break;
202                 case MSG_HIDE_SYSTEM_BAR_FOR_IMMERSIVE:
203                     int val = msg.arg1;
204                     Intent hideSysBarIntent = new Intent(REQUEST_FROM_LAUNCHER);
205                     Log.d(TAG, "hideSysBarIntent: val = " + val);
206 
207                     hideSysBarIntent.putExtra(INTENT_EXTRA_HIDE_SYSTEM_BAR_FOR_IMMERSIVE_MODE, val);
208                     CarUiPortraitService.this.sendBroadcast(hideSysBarIntent);
209                     break;
210                 case MSG_FG_TASK_VIEW_READY:
211                     Intent taskViewReadyIntent = new Intent(REQUEST_FROM_LAUNCHER);
212                     taskViewReadyIntent.putExtra(INTENT_EXTRA_FG_TASK_VIEW_READY,
213                             intToBoolean(msg.arg1));
214                     taskViewReadyIntent.putExtra(INTENT_EXTRA_LAUNCHER_READY,
215                             intToBoolean(msg.arg1));
216                     CarUiPortraitService.this.sendBroadcast(taskViewReadyIntent);
217                     break;
218                 default:
219                     super.handleMessage(msg);
220             }
221         }
222     }
223 
224     @Override
onCreate()225     public void onCreate() {
226         mSysUiRequestsReceiver = new BroadcastReceiver() {
227             @Override
228             public void onReceive(Context context, Intent intent) {
229                 boolean isImmersive = intent.getBooleanExtra(
230                         INTENT_EXTRA_IS_IMMERSIVE_MODE_REQUESTED, false);
231                 String source = intent.getStringExtra(
232                         INTENT_EXTRA_IMMERSIVE_MODE_REQUESTED_SOURCE);
233                 Log.d(TAG, "Immersive request: source = " + source + ", request=" + isImmersive);
234                 if (intent.hasExtra(INTENT_EXTRA_IS_IMMERSIVE_MODE_REQUESTED)
235                         && isImmersive != mIsSystemInImmersiveMode
236                         && source != null
237                         && !source.equals(mImmersiveModeSource)) {
238                     mIsSystemInImmersiveMode = isImmersive;
239                     mImmersiveModeSource = source;
240                     Bundle bundle = new Bundle();
241                     bundle.putString(INTENT_EXTRA_IMMERSIVE_MODE_REQUESTED_SOURCE, source);
242                     notifyClients(MSG_IMMERSIVE_MODE_REQUESTED, boolToInt(isImmersive), bundle);
243                 }
244 
245                 boolean isImmersiveState = intent.getBooleanExtra(
246                         INTENT_EXTRA_IS_IMMERSIVE_MODE_STATE, false);
247                 if (intent.hasExtra(INTENT_EXTRA_IS_IMMERSIVE_MODE_STATE)) {
248                     notifyClients(MSG_IMMERSIVE_MODE_CHANGE, boolToInt(isImmersiveState));
249                 }
250 
251                 boolean isSuwInProgress = intent.getBooleanExtra(
252                         INTENT_EXTRA_SUW_IN_PROGRESS, false);
253                 if (intent.hasExtra(INTENT_EXTRA_SUW_IN_PROGRESS)
254                         && isSuwInProgress != mIsSuwInProgress) {
255                     mIsSuwInProgress = isSuwInProgress;
256                     notifyClients(MSG_SUW_IN_PROGRESS, boolToInt(isSuwInProgress));
257                 }
258 
259                 if (intent.hasExtra(INTENT_EXTRA_COLLAPSE_APPLICATION_PANEL)) {
260                     notifyClients(MSG_COLLAPSE_APPLICATION, 1);
261                 }
262             }
263         };
264         IntentFilter filter = new IntentFilter();
265         filter.addAction(REQUEST_FROM_SYSTEM_UI);
266         registerReceiver(mSysUiRequestsReceiver, filter);
267         Log.d(TAG, "Portrait service is created");
268     }
269 
270     @Override
onBind(Intent intent)271     public IBinder onBind(Intent intent) {
272         return mMessenger.getBinder();
273     }
274 
275     @Override
onDestroy()276     public void onDestroy() {
277         super.onDestroy();
278         unregisterReceiver(mSysUiRequestsReceiver);
279     }
280 
notifyClients(int key, int value)281     private void notifyClients(int key, int value) {
282         for (int i = mClients.size() - 1; i >= 0; i--) {
283             try {
284                 mClients.get(i).send(Message.obtain(null, key, value, 0));
285             } catch (RemoteException e) {
286                 // The client is dead. Remove it from the list.
287                 mClients.remove(i);
288                 Log.d(TAG, "A client is removed from the list");
289             }
290         }
291     }
292 
notifyClients(int key, int value, Bundle bundle)293     private void notifyClients(int key, int value, Bundle bundle) {
294         for (int i = mClients.size() - 1; i >= 0; i--) {
295             try {
296                 Message msg = Message.obtain(null, key, value, 0);
297                 msg.setData(bundle);
298                 mClients.get(i).send(msg);
299             } catch (RemoteException e) {
300                 // The client is dead. Remove it from the list.
301                 mClients.remove(i);
302                 Log.d(TAG, "A client is removed from the list");
303             }
304         }
305     }
306 
intToBoolean(int val)307     private boolean intToBoolean(int val) {
308         return val == 1;
309     }
310 
boolToInt(Boolean b)311     private static int boolToInt(Boolean b) {
312         return b ? 1 : 0;
313     }
314 }
315