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