1 /* 2 * Copyright (C) 2016 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.stubs; 18 19 import android.app.ForegroundServiceStartNotAllowedException; 20 import android.app.Notification; 21 import android.app.NotificationChannel; 22 import android.app.NotificationManager; 23 import android.app.Service; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.pm.ServiceInfo; 27 import android.os.Binder; 28 import android.os.Bundle; 29 import android.os.Handler; 30 import android.os.IBinder; 31 import android.os.Message; 32 import android.os.Messenger; 33 import android.os.RemoteException; 34 import android.util.Log; 35 36 import com.android.compatibility.common.util.IBinderParcelable; 37 38 public class LocalForegroundService extends LocalService { 39 40 private static final String TAG = "LocalForegroundService"; 41 public static final String EXTRA_COMMAND = "LocalForegroundService.command"; 42 public static final String EXTRA_FOREGROUND_SERVICE_TYPE = "ForegroundService.type"; 43 public static final String NOTIFICATION_CHANNEL_ID = "cts/" + TAG; 44 public static String ACTION_START_FGS_RESULT = 45 "android.app.stubs.LocalForegroundService.RESULT"; 46 47 public static final int COMMAND_START_FOREGROUND = 1; 48 public static final int COMMAND_STOP_FOREGROUND_REMOVE_NOTIFICATION = 2; 49 public static final int COMMAND_STOP_FOREGROUND_DONT_REMOVE_NOTIFICATION = 3; 50 public static final int COMMAND_STOP_FOREGROUND_DETACH_NOTIFICATION = 4; 51 public static final int COMMAND_STOP_FOREGROUND_REMOVE_NOTIFICATION_USING_FLAGS = 5; 52 public static final int COMMAND_START_NO_FOREGROUND = 6; 53 public static final int COMMAND_START_FOREGROUND_DEFER_NOTIFICATION = 7; 54 public static final int COMMAND_STOP_SELF = 8; 55 56 private final Messenger mMessenger = new Messenger(new IncomingHandler()); 57 58 private int mNotificationId = 0; 59 60 private static final int DEFAULT_FGS_TYPE = ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE; 61 getTag()62 protected String getTag() { 63 return TAG; 64 } 65 66 @Override onCreate()67 public void onCreate() { 68 super.onCreate(); 69 Log.d(getTag(), "service created: " + this + " in " + android.os.Process.myPid()); 70 } 71 72 /** Returns the channel id for this service */ getNotificationChannelId()73 public static String getNotificationChannelId() { 74 return NOTIFICATION_CHANNEL_ID; 75 } 76 77 @Override onStartCommand(Intent intent, int flags, int startId)78 public int onStartCommand(Intent intent, int flags, int startId) { 79 String notificationChannelId = getNotificationChannelId(); 80 NotificationManager notificationManager = getSystemService(NotificationManager.class); 81 notificationManager.createNotificationChannel(new NotificationChannel( 82 notificationChannelId, notificationChannelId, 83 NotificationManager.IMPORTANCE_DEFAULT)); 84 85 Context context = getApplicationContext(); 86 final int command = intent.getIntExtra(EXTRA_COMMAND, -1); 87 88 Log.d(getTag(), "service start cmd " + command + ", intent " + intent); 89 90 switch (command) { 91 case COMMAND_START_FOREGROUND: 92 case COMMAND_START_FOREGROUND_DEFER_NOTIFICATION: { 93 handleIncomingMessengerIfNeeded(intent); 94 mNotificationId ++; 95 final boolean showNow = (command == COMMAND_START_FOREGROUND); 96 Log.d(getTag(), "Starting foreground using notification " + mNotificationId); 97 Notification.Builder builder = 98 new Notification.Builder(context, NOTIFICATION_CHANNEL_ID) 99 .setContentTitle(getNotificationTitle(mNotificationId)) 100 .setSmallIcon(R.drawable.black); 101 if (showNow) { 102 builder.setForegroundServiceBehavior( 103 Notification.FOREGROUND_SERVICE_IMMEDIATE); 104 } 105 final int fgsType = intent.getIntExtra(EXTRA_FOREGROUND_SERVICE_TYPE, 106 DEFAULT_FGS_TYPE); 107 try { 108 startForeground(mNotificationId, builder.build(), fgsType); 109 } catch (ForegroundServiceStartNotAllowedException e) { 110 Log.d(TAG, "startForeground gets an " 111 + " ForegroundServiceStartNotAllowedException", e); 112 } 113 break; 114 } 115 case COMMAND_STOP_FOREGROUND_REMOVE_NOTIFICATION: 116 Log.d(getTag(), "Stopping foreground removing notification"); 117 stopForeground(true); 118 break; 119 case COMMAND_STOP_FOREGROUND_DONT_REMOVE_NOTIFICATION: 120 Log.d(getTag(), "Stopping foreground without removing notification"); 121 stopForeground(false); 122 break; 123 case COMMAND_STOP_FOREGROUND_REMOVE_NOTIFICATION_USING_FLAGS: 124 Log.d(getTag(), "Stopping foreground removing notification using flags"); 125 stopForeground(Service.STOP_FOREGROUND_REMOVE | Service.STOP_FOREGROUND_DETACH); 126 break; 127 case COMMAND_STOP_FOREGROUND_DETACH_NOTIFICATION: 128 Log.d(getTag(), "Detaching foreground service notification"); 129 stopForeground(Service.STOP_FOREGROUND_DETACH); 130 break; 131 case COMMAND_START_NO_FOREGROUND: 132 Log.d(getTag(), "Starting without calling startForeground()"); 133 break; 134 default: 135 Log.e(getTag(), "Unknown command: " + command); 136 } 137 138 sendBroadcast( 139 new Intent(ACTION_START_FGS_RESULT).setFlags(Intent.FLAG_RECEIVER_FOREGROUND)); 140 141 // Do parent's onStart at the end, so we don't race with the test code waiting for us to 142 // execute. 143 super.onStart(intent, startId); 144 return START_NOT_STICKY; 145 } 146 147 @Override onDestroy()148 public void onDestroy() { 149 Log.d(getTag(), "service destroyed: " + this + " in " + android.os.Process.myPid()); 150 super.onDestroy(); 151 } 152 newCommand(IBinder stateReceiver, int command)153 public static Bundle newCommand(IBinder stateReceiver, int command) { 154 Bundle bundle = new Bundle(); 155 bundle.putParcelable(LocalService.REPORT_OBJ_NAME, new IBinderParcelable(stateReceiver)); 156 bundle.putInt(EXTRA_COMMAND, command); 157 return bundle; 158 } 159 newCommand(int command)160 public static Bundle newCommand(int command) { 161 Bundle bundle = new Bundle(); 162 bundle.putParcelable(LocalService.REPORT_OBJ_NAME, new IBinderParcelable(new Binder())); 163 bundle.putInt(EXTRA_COMMAND, command); 164 return bundle; 165 } 166 getNotificationTitle(int id)167 public static String getNotificationTitle(int id) { 168 return "I AM FOREGROOT #" + id; 169 } 170 171 /** 172 * Check if the given {@code intent} has embodied a messenger object which is to receive 173 * the messenger interface based controller, if so, send our {@link #mMessenger} to it. 174 */ handleIncomingMessengerIfNeeded(final Intent intent)175 private void handleIncomingMessengerIfNeeded(final Intent intent) { 176 final Bundle extras = intent.getExtras(); 177 if (extras != null) { 178 final IBinder binder = extras.getBinder(CommandReceiver.EXTRA_MESSENGER); 179 if (binder != null) { 180 final Messenger messenger = new Messenger(binder); 181 final Bundle reply = new Bundle(); 182 final Message msg = Message.obtain(); 183 msg.obj = reply; 184 reply.putBinder(CommandReceiver.EXTRA_MESSENGER, mMessenger.getBinder()); 185 try { 186 messenger.send(msg); 187 } catch (RemoteException e) { 188 Log.e(TAG, "Unable to send back the messenger controller interface"); 189 } 190 msg.recycle(); 191 } 192 } 193 } 194 195 private class IncomingHandler extends Handler { handleMessage(Message msg)196 public void handleMessage(Message msg) { 197 switch (msg.what) { 198 case COMMAND_STOP_SELF: 199 Log.d(TAG, "Stopping self"); 200 stopSelf(); 201 break; 202 default: 203 Log.e(TAG, "Unsupported command via messenger interface: " + msg.what); 204 break; 205 } 206 } 207 } 208 } 209