1 /*
2  * Copyright (C) 2020 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.media.testmediaapp;
18 
19 import android.app.Notification;
20 import android.app.NotificationChannel;
21 import android.app.NotificationManager;
22 import android.app.PendingIntent;
23 import android.app.Service;
24 import android.content.Intent;
25 import android.location.Location;
26 import android.location.LocationListener;
27 import android.location.LocationManager;
28 import android.os.Bundle;
29 import android.os.IBinder;
30 import android.widget.Toast;
31 
32 import androidx.annotation.Nullable;
33 import androidx.core.app.NotificationCompat;
34 
35 import com.android.car.media.testmediaapp.prefs.TmaPrefsActivity;
36 
37 /**
38  * Service used to test and demonstrate the access to "foreground" permissions. In particular, this
39  * implementation deals with location access from a headless service. This service is initiated
40  * using {@link Service#startService(Intent)} from the browse service as a respond to a custom
41  * playback command. Subsequent start commands make the service toggle between running and stopping.
42  *
43  * In real applications, this service would be handling background playback, maybe using location
44  * and other sensors to automatically select songs.
45  */
46 public class TmaForegroundService extends Service {
47     public static final String CHANNEL_ID = "ForegroundServiceChannel";
48     private LocationManager mLocationManager;
49 
50     @Override
onCreate()51     public void onCreate() {
52         super.onCreate();
53     }
54 
55     @Override
onStartCommand(Intent intent, int flags, int startId)56     public int onStartCommand(Intent intent, int flags, int startId) {
57         if (doWork()) {
58             createNotificationChannel();
59             Intent notificationIntent = new Intent(this, TmaPrefsActivity.class);
60             PendingIntent pendingIntent = PendingIntent.getActivity(this,
61                     0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
62             Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
63                     .setContentTitle("Foreground Service")
64                     .setSmallIcon(R.drawable.ic_app_icon)
65                     .setContentIntent(pendingIntent)
66                     .build();
67             startForeground(1, notification);
68         } else {
69             getMainExecutor().execute(this::stopSelf);
70         }
71 
72         return START_NOT_STICKY;
73     }
74 
75     @Override
onDestroy()76     public void onDestroy() {
77         if (mLocationManager != null) {
78             mLocationManager.removeUpdates(mLocationListener);
79             toast("Location is off");
80         }
81         super.onDestroy();
82     }
83 
84     @Nullable
85     @Override
onBind(Intent intent)86     public IBinder onBind(Intent intent) {
87         return null;
88     }
89 
createNotificationChannel()90     private void createNotificationChannel() {
91         NotificationChannel serviceChannel = new NotificationChannel(
92                 CHANNEL_ID,
93                 "Foreground Service Channel",
94                 NotificationManager.IMPORTANCE_DEFAULT
95         );
96         NotificationManager manager = getSystemService(NotificationManager.class);
97         manager.createNotificationChannel(serviceChannel);
98     }
99 
doWork()100     private boolean doWork() {
101         if (mLocationManager != null) {
102             return false;
103         }
104         mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
105         try {
106             mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2000,
107                     0, mLocationListener);
108             toast("Location is on");
109         } catch (Throwable e) {
110             toast("Unable to get location: " + e.getMessage());
111         }
112         return true;
113     }
114 
115     /**
116      * We use toasts here as it is the only way for a headless service to show something on the
117      * screen. Real application shouldn't be using toasts from service.
118      */
toast(String message)119     private void toast(String message) {
120         Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
121     }
122 
123     private final LocationListener mLocationListener = new LocationListener() {
124         @Override
125         public void onLocationChanged(Location location) {
126             toast("Location provider: " + location.getLatitude() + ":" + location.getLongitude());
127         }
128 
129         @Override
130         public void onStatusChanged(String provider, int status, Bundle extras) {
131             toast("Location provider: " + provider + " status changed to: " + status);
132         }
133 
134         @Override
135         public void onProviderEnabled(String provider) {
136             toast("Location provider enabled: " + provider);
137         }
138 
139         @Override
140         public void onProviderDisabled(String provider) {
141             toast("Location provider disabled: " + provider);
142         }
143     };
144 }
145