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 android.app.fgstesthelper;
18 
19 import android.app.ForegroundServiceStartNotAllowedException;
20 import android.app.InvalidForegroundServiceTypeException;
21 import android.app.MissingForegroundServiceTypeException;
22 import android.app.Notification;
23 import android.app.NotificationChannel;
24 import android.app.NotificationManager;
25 import android.app.Service;
26 import android.content.Intent;
27 import android.content.pm.ServiceInfo;
28 import android.os.IBinder;
29 import android.os.Process;
30 import android.util.Log;
31 
32 /**
33  * A foreground service without a specific type.
34  */
35 public abstract class LocalForegroundServiceBase extends Service {
36     private static final String TAG = "LocalForegroundServiceBase";
37     private static final String NOTIFICATION_CHANNEL_ID = "cts/" + TAG;
38 
39     public static final String EXTRA_COMMAND = "LocalForegroundService.command";
40     public static final String EXTRA_FGS_TYPE = "LocalForegroundService.fgs_type";
41     public static final String EXTRA_RESULT_CODE = "LocalForegroundService.result_code";
42     public static String ACTION_START_FGS_RESULT =
43             "android.app.fgs.LocalForegroundService.RESULT";
44 
45     public static final int COMMAND_START_FOREGROUND = 1;
46     public static final int COMMAND_START_BACKGROUND = 2;
47     public static final int COMMAND_SET_FOREGROUND = 3;
48     public static final int COMMAND_STOP_SELF = 4;
49 
50     public static final int RESULT_OK = 0;
51     public static final int RESULT_START_EXCEPTION = 1;
52     public static final int RESULT_INVALID_TYPE_EXCEPTION = 2;
53     public static final int RESULT_MISSING_TYPE_EXCEPTION = 3;
54     public static final int RESULT_SECURITY_EXCEPTION = 4;
55 
56     private int mNotificationId = 0;
57 
58     @Override
onCreate()59     public void onCreate() {
60         super.onCreate();
61         Log.d(getTag(), "service created: " + this + " in " + Process.myPid());
62     }
63 
64     @Override
onBind(Intent intent)65     public IBinder onBind(Intent intent) {
66         return null;
67     }
68 
getTag()69     String getTag() {
70         return TAG;
71     }
72 
73     /** Returns the channel id for this service */
getNotificationChannelId()74     String getNotificationChannelId() {
75         return NOTIFICATION_CHANNEL_ID;
76     }
77 
getNotificationTitle(int id)78     String getNotificationTitle(int id) {
79         return "I AM FOREGROOT #" + id;
80     }
81 
getNotificationIcon()82     abstract int getNotificationIcon();
83 
84     @Override
onStartCommand(Intent intent, int flags, int startId)85     public int onStartCommand(Intent intent, int flags, int startId) {
86         final int command = intent.getIntExtra(EXTRA_COMMAND, -1);
87 
88         Log.d(getTag(), "service start cmd " + command + ", intent " + intent);
89 
90         int result = RESULT_OK;
91         switch (command) {
92             case COMMAND_START_FOREGROUND: {
93                 Log.d(getTag(), "Calling startForeground()");
94                 result = startForeground(intent.getIntExtra(EXTRA_FGS_TYPE,
95                         ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST));
96                 break;
97             }
98             case COMMAND_START_BACKGROUND:
99                 Log.d(getTag(), "Starting without calling startForeground()");
100                 break;
101             case COMMAND_SET_FOREGROUND:
102                 Log.d(getTag(), "Calling startForeground() separately");
103                 result = startForeground(intent.getIntExtra(EXTRA_FGS_TYPE,
104                         ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST));
105                 break;
106             case COMMAND_STOP_SELF:
107                 Log.d(getTag(), "Calling stopSelf()");
108                 stopSelf(startId);
109                 return START_NOT_STICKY;
110             default:
111                 Log.e(getTag(), "Unknown command: " + command);
112         }
113 
114         final Intent reply = new Intent(ACTION_START_FGS_RESULT);
115         reply.putExtra(EXTRA_RESULT_CODE, result);
116         reply.setFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
117         sendBroadcast(reply);
118 
119         return START_NOT_STICKY;
120     }
121 
startForeground(int type)122     private int startForeground(int type) {
123         final String notificationChannelId = getNotificationChannelId();
124         NotificationManager notificationManager = getSystemService(NotificationManager.class);
125         notificationManager.createNotificationChannel(new NotificationChannel(
126                 notificationChannelId, notificationChannelId,
127                 NotificationManager.IMPORTANCE_DEFAULT));
128 
129         mNotificationId++;
130         Log.d(getTag(), "Starting foreground using notification " + mNotificationId);
131         Notification.Builder builder =
132                 new Notification.Builder(this, notificationChannelId)
133                         .setContentTitle(getNotificationTitle(mNotificationId))
134                         .setSmallIcon(getNotificationIcon());
135         try {
136             startForeground(mNotificationId, builder.build(), type);
137         } catch (ForegroundServiceStartNotAllowedException e) {
138             // Not expected.
139             Log.d(getTag(), "startForeground gets an "
140                     + " ForegroundServiceStartNotAllowedException", e);
141             return RESULT_START_EXCEPTION;
142         } catch (InvalidForegroundServiceTypeException e) {
143             Log.d(getTag(), "startForeground gets an "
144                     + " InvalidForegroundServiceTypeException " + e);
145             return RESULT_INVALID_TYPE_EXCEPTION;
146         } catch (MissingForegroundServiceTypeException e) {
147             Log.d(getTag(), "startForeground gets an "
148                     + " MissingForegroundServiceTypeException " + e);
149             return RESULT_MISSING_TYPE_EXCEPTION;
150         } catch (SecurityException e) {
151             Log.d(getTag(), "startForeground gets an "
152                     + " SecurityException " + e);
153             return RESULT_SECURITY_EXCEPTION;
154         }
155         return RESULT_OK;
156     }
157 
158     @Override
onDestroy()159     public void onDestroy() {
160         Log.d(getTag(), "service destroyed: " + this + " in " + Process.myPid());
161         super.onDestroy();
162     }
163 }
164