• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 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 package com.android.google.gce.gceservice;
17 
18 import android.app.Notification;
19 import android.app.NotificationChannel;
20 import android.app.NotificationManager;
21 import android.app.Service;
22 import android.bluetooth.BluetoothAdapter;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.res.Configuration;
27 import android.graphics.Point;
28 import android.net.ConnectivityManager;
29 import android.util.Log;
30 import android.os.IBinder;
31 import android.view.Display;
32 import android.view.Surface;
33 import android.view.WindowManager;
34 import java.io.FileDescriptor;
35 import java.io.PrintWriter;
36 import java.util.List;
37 
38 /**
39  * Service is started by the BootCompletedReceiver at the end of Android Boot process.
40  * Responsible for emitting final BOOT_COMPLETED message and continued configuration changes.
41  */
42 public class GceService extends Service {
43     private static final String LOG_TAG = "GceService";
44     /* Intent sent by the BootCompletedReceiver upon receiving ACTION_BOOT_COMPLETED broadcast. */
45     public static final String INTENT_ACTION_CONFIGURE = "com.android.google.gce.gceservice.CONFIGURE";
46     public static final String INTENT_ACTION_NETWORK_CHANGED = "com.android.google.gce.gceservice.NETWORK_CHANGED";
47     public static final String INTENT_ACTION_BLUETOOTH_CHANGED = "com.android.google.gce.gceservice.BLUETOOTH_CHANGED";
48     private static final String NOTIFICATION_CHANNEL_ID = "cuttlefish-service";
49     private static final String NOTIFICATION_CHANNEL_NAME = "Cuttlefish Service";
50     private static final int NOTIFICATION_ID = 1;
51 
52     private final JobExecutor mExecutor = new JobExecutor();
53     private final EventReporter mEventReporter = new EventReporter();
54     private final GceBroadcastReceiver mBroadcastReceiver = new GceBroadcastReceiver();
55 
56     private BluetoothChecker mBluetoothChecker = null;
57     private ConnectivityChecker mConnChecker;
58     private GceWifiManager mWifiManager = null;
59     private String mMostRecentAction = null;
60     private WindowManager mWindowManager;
61 
62     private int mPreviousRotation;
63     private Point mPreviousScreenBounds;
64     private int mPreviousDpi;
65 
66 
GceService()67     public GceService() {}
68 
69 
70     @Override
onCreate()71     public void onCreate() {
72         try {
73             super.onCreate();
74             mEventReporter.reportBootStarted();
75             registerBroadcastReceivers();
76 
77             mWindowManager = getSystemService(WindowManager.class);
78             mConnChecker = new ConnectivityChecker(this, mEventReporter);
79             mWifiManager = new GceWifiManager(this, mEventReporter, mExecutor);
80             mBluetoothChecker = new BluetoothChecker(this);
81 
82             mPreviousRotation = getRotation();
83             mPreviousScreenBounds = getScreenBounds();
84             mPreviousDpi = getResources().getConfiguration().densityDpi;
85 
86             mExecutor.schedule(mWifiManager);
87             mExecutor.schedule(mBluetoothChecker);
88             mExecutor.schedule(mConnChecker);
89 
90             mExecutor.schedule(mEventReporter, mBluetoothChecker.getEnabled());
91 
92             NotificationManager notificationManager =
93                     (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
94             NotificationChannel channel =
95                     new NotificationChannel(
96                             NOTIFICATION_CHANNEL_ID,
97                             NOTIFICATION_CHANNEL_NAME,
98                             NotificationManager.IMPORTANCE_LOW);
99             notificationManager.createNotificationChannel(channel);
100 
101         } catch (Exception e) {
102             Log.e(LOG_TAG, "Exception caught", e);
103         }
104     }
105 
106 
107     @Override
onBind(Intent intent)108     public IBinder onBind(Intent intent) {
109         return null;
110     }
111 
112 
113     /** Register broadcast listeners.
114      *
115      * Certain intents can no longer be used to start a service or activity, but
116      * can still be registered for, such as CONNECTIVITY_ACTION (Android N).
117      */
registerBroadcastReceivers()118     private void registerBroadcastReceivers() {
119         IntentFilter filter = new IntentFilter();
120         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
121         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
122         this.registerReceiver(mBroadcastReceiver, filter);
123     }
124 
getScreenBounds()125     private Point getScreenBounds() {
126         Display display = mWindowManager.getDefaultDisplay();
127         Point screenBounds = new Point();
128         display.getRealSize(screenBounds);
129         return screenBounds;
130     }
131 
getRotation()132     private int getRotation() {
133       int rot = mWindowManager.getDefaultDisplay().getRotation();
134       switch (rot) {
135         case Surface.ROTATION_0:
136           return 0;
137         case Surface.ROTATION_90:
138           return 90;
139         case Surface.ROTATION_180:
140           return 180;
141         case Surface.ROTATION_270:
142           return 270;
143       }
144       throw new IllegalStateException("Rotation should be one of 0,90,180,270");
145     }
146 
147     @Override
onConfigurationChanged(Configuration config)148     public void onConfigurationChanged(Configuration config) {
149         super.onConfigurationChanged(config);
150 
151         int rotation = getRotation();
152         Point screenBounds = getScreenBounds();
153         int dpi = config.densityDpi;
154         // NOTE: We cannot rely on config.diff(previous config) here because
155         // diff shows CONFIG_SCREEN_SIZE changes when changing between 3-button
156         // and gesture navigation. We only care about the display bounds.
157         if (rotation == mPreviousRotation &&
158             screenBounds.equals(mPreviousScreenBounds) &&
159             dpi == mPreviousDpi) {
160             return;
161         }
162 
163         int width = screenBounds.x;
164         int height = screenBounds.y;
165         mEventReporter.reportScreenChanged(width, height, dpi, rotation);
166 
167         mPreviousRotation = rotation;
168         mPreviousScreenBounds = screenBounds;
169         mPreviousDpi = dpi;
170     }
171 
172 
173     /** StartService entry point.
174      */
175     @Override
onStartCommand(Intent intent, int flags, int startId)176     public int onStartCommand(Intent intent, int flags, int startId) {
177         if (intent != null) {
178             mMostRecentAction = intent.getAction();
179         } else {
180             Log.w(LOG_TAG, "Previous execution failed. Retrying.");
181         }
182 
183         if (mMostRecentAction == null) {
184             Log.e(LOG_TAG, "Missing intent action.");
185         }
186 
187         Notification notification =
188                 new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
189                         .setAutoCancel(true)
190                         .setContentTitle("Cuttlefish service is running.")
191                         .setSmallIcon(android.R.drawable.ic_dialog_info)
192                         .setTimeoutAfter(10000)
193                         .build();
194         // Start in the Foreground (and do not stop) so that this service
195         // continues running and reporting events without being killed.
196         startForeground(NOTIFICATION_ID, notification);
197 
198         if (INTENT_ACTION_CONFIGURE.equals(mMostRecentAction)) {
199             mExecutor.schedule(mConnChecker);
200         } else if (INTENT_ACTION_NETWORK_CHANGED.equals(mMostRecentAction)) {
201             mExecutor.schedule(mConnChecker);
202         } else if (INTENT_ACTION_BLUETOOTH_CHANGED.equals(mMostRecentAction)) {
203             mExecutor.schedule(mBluetoothChecker);
204         }
205 
206         /* If anything goes wrong, make sure we receive intent again. */
207         return Service.START_STICKY;
208     }
209 
210     @Override
onDestroy()211     public void onDestroy() {
212         unregisterReceiver(mBroadcastReceiver);
213     }
214 
215     /** Dump the virtual device state
216      */
217     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)218     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
219         pw.println("Virtual Device reporter:");
220         List<String> messageList = mEventReporter.getMessageList();
221         for (int i = 0; i < messageList.size(); i++) {
222             pw.println("  " + messageList.get(i));
223         }
224         pw.println("");
225         pw.println("Current system service state:");
226         pw.println("  Network connected: " + mConnChecker.getConnected().isDone());
227         pw.println("  WiFi configured: " + mWifiManager.getWifiReady().isDone());
228         pw.println("  Bluetooth enabled: " + mBluetoothChecker.getEnabled().isDone());
229         pw.println("");
230     }
231 }
232