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