1 /* 2 * Copyright (C) 2018 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.Activity; 20 import android.app.ActivityManager; 21 import android.app.ForegroundServiceStartNotAllowedException; 22 import android.app.PendingIntent; 23 import android.content.BroadcastReceiver; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.ServiceConnection; 28 import android.os.Bundle; 29 import android.os.IBinder; 30 import android.os.Parcel; 31 import android.util.ArrayMap; 32 import android.util.Log; 33 34 import java.util.concurrent.TimeUnit; 35 36 public class CommandReceiver extends BroadcastReceiver { 37 38 private static final String TAG = "CommandReceiver"; 39 40 // Requires flags and targetPackage 41 public static final int COMMAND_BIND_SERVICE = 1; 42 // Requires targetPackage 43 public static final int COMMAND_UNBIND_SERVICE = 2; 44 public static final int COMMAND_START_FOREGROUND_SERVICE = 3; 45 public static final int COMMAND_STOP_FOREGROUND_SERVICE = 4; 46 public static final int COMMAND_START_FOREGROUND_SERVICE_LOCATION = 5; 47 public static final int COMMAND_STOP_FOREGROUND_SERVICE_LOCATION = 6; 48 public static final int COMMAND_START_ALERT_SERVICE = 7; 49 public static final int COMMAND_STOP_ALERT_SERVICE = 8; 50 public static final int COMMAND_SELF_INDUCED_ANR = 9; 51 public static final int COMMAND_START_ACTIVITY = 10; 52 public static final int COMMAND_STOP_ACTIVITY = 11; 53 public static final int COMMAND_CREATE_FGSL_PENDING_INTENT = 12; 54 public static final int COMMAND_SEND_FGSL_PENDING_INTENT = 13; 55 public static final int COMMAND_BIND_FOREGROUND_SERVICE = 14; 56 public static final int COMMAND_START_CHILD_PROCESS = 15; 57 public static final int COMMAND_STOP_CHILD_PROCESS = 16; 58 public static final int COMMAND_WAIT_FOR_CHILD_PROCESS_GONE = 17; 59 public static final int COMMAND_START_SERVICE = 18; 60 public static final int COMMAND_STOP_SERVICE = 19; 61 public static final int COMMAND_START_FOREGROUND_SERVICE_STICKY = 20; 62 public static final int COMMAND_STOP_FOREGROUND_SERVICE_STICKY = 21; 63 public static final int COMMAND_EMPTY = 22; 64 65 public static final int RESULT_CHILD_PROCESS_STARTED = IBinder.FIRST_CALL_TRANSACTION; 66 public static final int RESULT_CHILD_PROCESS_STOPPED = IBinder.FIRST_CALL_TRANSACTION + 1; 67 public static final int RESULT_CHILD_PROCESS_GONE = IBinder.FIRST_CALL_TRANSACTION + 2; 68 69 public static final String EXTRA_COMMAND = "android.app.stubs.extra.COMMAND"; 70 public static final String EXTRA_TARGET_PACKAGE = "android.app.stubs.extra.TARGET_PACKAGE"; 71 public static final String EXTRA_FLAGS = "android.app.stubs.extra.FLAGS"; 72 public static final String EXTRA_CALLBACK = "android.app.stubs.extra.callback"; 73 public static final String EXTRA_CHILD_CMDLINE = "android.app.stubs.extra.child_cmdline"; 74 public static final String EXTRA_TIMEOUT = "android.app.stubs.extra.child_cmdline"; 75 public static final String EXTRA_MESSENGER = "android.app.stubs.extra.EXTRA_MESSENGER"; 76 77 public static final String SERVICE_NAME = "android.app.stubs.LocalService"; 78 public static final String FG_SERVICE_NAME = "android.app.stubs.LocalForegroundService"; 79 public static final String FG_LOCATION_SERVICE_NAME = 80 "android.app.stubs.LocalForegroundServiceLocation"; 81 public static final String FG_STICKY_SERVICE_NAME = 82 "android.app.stubs.LocalForegroundServiceSticky"; 83 84 public static final String ACTIVITY_NAME = "android.app.stubs.SimpleActivity"; 85 86 private static ArrayMap<String,ServiceConnection> sServiceMap = new ArrayMap<>(); 87 88 // Map a packageName to a Intent that starts an Activity. 89 private static ArrayMap<String, Intent> sActivityIntent = new ArrayMap<>(); 90 91 // Map a packageName to a PendingIntent. 92 private static ArrayMap<String, PendingIntent> sPendingIntent = new ArrayMap<>(); 93 94 /** The child process, started via {@link #COMMAND_START_CHILD_PROCESS} */ 95 private static Process sChildProcess; 96 97 /** 98 * Handle the different types of binding/unbinding requests. 99 * @param context The Context in which the receiver is running. 100 * @param intent The Intent being received. 101 */ 102 @Override onReceive(Context context, Intent intent)103 public void onReceive(Context context, Intent intent) { 104 // Use the application context as the receiver context could be restricted. 105 context = context.getApplicationContext(); 106 int command = intent.getIntExtra(EXTRA_COMMAND, -1); 107 Log.d(TAG + "_" + context.getPackageName(), "Got command " + command + ", intent=" 108 + intent); 109 switch (command) { 110 case COMMAND_BIND_SERVICE: 111 doBindService(context, intent, SERVICE_NAME); 112 break; 113 case COMMAND_UNBIND_SERVICE: 114 doUnbindService(context, intent); 115 break; 116 case COMMAND_START_FOREGROUND_SERVICE: 117 doStartForegroundService(context, intent); 118 break; 119 case COMMAND_START_SERVICE: 120 doStartService(context, intent); 121 break; 122 case COMMAND_STOP_FOREGROUND_SERVICE: 123 case COMMAND_STOP_SERVICE: 124 doStopService(context, intent, FG_SERVICE_NAME); 125 break; 126 case COMMAND_START_FOREGROUND_SERVICE_LOCATION: 127 doStartForegroundServiceWithType(context, intent); 128 break; 129 case COMMAND_STOP_FOREGROUND_SERVICE_LOCATION: 130 doStopService(context, intent, FG_LOCATION_SERVICE_NAME); 131 break; 132 case COMMAND_START_FOREGROUND_SERVICE_STICKY: 133 doStartForegroundServiceSticky(context, intent); 134 break; 135 case COMMAND_STOP_FOREGROUND_SERVICE_STICKY: 136 doStopService(context, intent, FG_STICKY_SERVICE_NAME); 137 break; 138 case COMMAND_START_ALERT_SERVICE: 139 doStartAlertService(context); 140 break; 141 case COMMAND_STOP_ALERT_SERVICE: 142 doStopAlertService(context); 143 break; 144 case COMMAND_SELF_INDUCED_ANR: 145 doSelfInducedAnr(context); 146 break; 147 case COMMAND_START_ACTIVITY: 148 doStartActivity(context, intent); 149 break; 150 case COMMAND_STOP_ACTIVITY: 151 doStopActivity(context, intent); 152 break; 153 case COMMAND_CREATE_FGSL_PENDING_INTENT: 154 doCreateFgslPendingIntent(context, intent); 155 break; 156 case COMMAND_SEND_FGSL_PENDING_INTENT: 157 doSendFgslPendingIntent(context, intent); 158 break; 159 case COMMAND_BIND_FOREGROUND_SERVICE: 160 doBindService(context, intent, FG_LOCATION_SERVICE_NAME); 161 break; 162 case COMMAND_START_CHILD_PROCESS: 163 doStartChildProcess(context, intent); 164 break; 165 case COMMAND_STOP_CHILD_PROCESS: 166 doStopChildProcess(context, intent); 167 break; 168 case COMMAND_WAIT_FOR_CHILD_PROCESS_GONE: 169 doWaitForChildProcessGone(context, intent); 170 break; 171 case COMMAND_EMPTY: 172 break; 173 } 174 } 175 doBindService(Context context, Intent commandIntent, String serviceName)176 private void doBindService(Context context, Intent commandIntent, String serviceName) { 177 String targetPackage = getTargetPackage(commandIntent); 178 int flags = getFlags(commandIntent); 179 180 Intent bindIntent = new Intent(); 181 bindIntent.setComponent(new ComponentName(targetPackage, serviceName)); 182 183 ServiceConnection connection = addServiceConnection(targetPackage); 184 185 context.bindService(bindIntent, connection, flags | Context.BIND_AUTO_CREATE); 186 } 187 doUnbindService(Context context, Intent commandIntent)188 private void doUnbindService(Context context, Intent commandIntent) { 189 String targetPackage = getTargetPackage(commandIntent); 190 context.unbindService(sServiceMap.remove(targetPackage)); 191 } 192 doStartForegroundService(Context context, Intent commandIntent)193 private void doStartForegroundService(Context context, Intent commandIntent) { 194 String targetPackage = getTargetPackage(commandIntent); 195 Intent fgsIntent = new Intent(); 196 fgsIntent.putExtras(commandIntent); 197 fgsIntent.setComponent(new ComponentName(targetPackage, FG_SERVICE_NAME)); 198 int command = LocalForegroundService.COMMAND_START_FOREGROUND; 199 fgsIntent.putExtras(LocalForegroundService.newCommand(command)); 200 try { 201 context.startForegroundService(fgsIntent); 202 } catch (ForegroundServiceStartNotAllowedException e) { 203 Log.d(TAG, "startForegroundService gets an " 204 + " ForegroundServiceStartNotAllowedException", e); 205 } 206 } 207 doStartService(Context context, Intent commandIntent)208 private void doStartService(Context context, Intent commandIntent) { 209 String targetPackage = getTargetPackage(commandIntent); 210 Intent fgsIntent = new Intent(); 211 fgsIntent.putExtras(commandIntent); 212 fgsIntent.setComponent(new ComponentName(targetPackage, FG_SERVICE_NAME)); 213 context.startService(fgsIntent); 214 } 215 doStartForegroundServiceWithType(Context context, Intent commandIntent)216 private void doStartForegroundServiceWithType(Context context, Intent commandIntent) { 217 String targetPackage = getTargetPackage(commandIntent); 218 Intent fgsIntent = new Intent(); 219 fgsIntent.putExtras(commandIntent); // include the fg service type if any. 220 fgsIntent.setComponent(new ComponentName(targetPackage, FG_LOCATION_SERVICE_NAME)); 221 int command = LocalForegroundServiceLocation.COMMAND_START_FOREGROUND_WITH_TYPE; 222 fgsIntent.putExtras(LocalForegroundService.newCommand(command)); 223 try { 224 context.startForegroundService(fgsIntent); 225 } catch (ForegroundServiceStartNotAllowedException e) { 226 Log.d(TAG, "startForegroundService gets an " 227 + "ForegroundServiceStartNotAllowedException", e); 228 } 229 } 230 doStartForegroundServiceSticky(Context context, Intent commandIntent)231 private void doStartForegroundServiceSticky(Context context, Intent commandIntent) { 232 String targetPackage = getTargetPackage(commandIntent); 233 Intent fgsIntent = new Intent(); 234 fgsIntent.putExtras(commandIntent); 235 fgsIntent.setComponent(new ComponentName(targetPackage, FG_STICKY_SERVICE_NAME)); 236 int command = LocalForegroundService.COMMAND_START_FOREGROUND; 237 fgsIntent.putExtras(LocalForegroundService.newCommand(command)); 238 try { 239 context.startForegroundService(fgsIntent); 240 } catch (ForegroundServiceStartNotAllowedException e) { 241 Log.d(TAG, "startForegroundService gets an " 242 + "ForegroundServiceStartNotAllowedException", e); 243 } 244 } 245 doStopService(Context context, Intent commandIntent, String serviceName)246 private void doStopService(Context context, Intent commandIntent, 247 String serviceName) { 248 String targetPackage = getTargetPackage(commandIntent); 249 Intent fgsIntent = new Intent(); 250 fgsIntent.setComponent(new ComponentName(targetPackage, serviceName)); 251 context.stopService(fgsIntent); 252 } 253 doStartAlertService(Context context)254 private void doStartAlertService(Context context) { 255 Intent intent = new Intent(context, LocalAlertService.class); 256 intent.setAction(LocalAlertService.COMMAND_SHOW_ALERT); 257 context.startService(intent); 258 } 259 doStopAlertService(Context context)260 private void doStopAlertService(Context context) { 261 Intent intent = new Intent(context, LocalAlertService.class); 262 intent.setAction(LocalAlertService.COMMAND_HIDE_ALERT); 263 context.startService(intent); 264 } 265 doSelfInducedAnr(Context context)266 private void doSelfInducedAnr(Context context) { 267 ActivityManager am = context.getSystemService(ActivityManager.class); 268 am.appNotResponding("CTS - self induced"); 269 } 270 doStartActivity(Context context, Intent commandIntent)271 private void doStartActivity(Context context, Intent commandIntent) { 272 String targetPackage = getTargetPackage(commandIntent); 273 Intent activityIntent = new Intent(Intent.ACTION_MAIN); 274 sActivityIntent.put(targetPackage, activityIntent); 275 activityIntent.putExtras(commandIntent); 276 activityIntent.setComponent(new ComponentName(targetPackage, ACTIVITY_NAME)); 277 activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 278 context.startActivity(activityIntent); 279 } 280 doStopActivity(Context context, Intent commandIntent)281 private void doStopActivity(Context context, Intent commandIntent) { 282 String targetPackage = getTargetPackage(commandIntent); 283 Intent activityIntent = sActivityIntent.remove(targetPackage); 284 activityIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); 285 activityIntent.putExtra("finish", true); 286 context.startActivity(activityIntent); 287 } 288 doCreateFgslPendingIntent(Context context, Intent commandIntent)289 private void doCreateFgslPendingIntent(Context context, Intent commandIntent) { 290 final String targetPackage = getTargetPackage(commandIntent); 291 final Intent intent = new Intent().setComponent( 292 new ComponentName(targetPackage, FG_LOCATION_SERVICE_NAME)); 293 int command = LocalForegroundServiceLocation.COMMAND_START_FOREGROUND_WITH_TYPE; 294 intent.putExtras(LocalForegroundService.newCommand(command)); 295 final PendingIntent pendingIntent = PendingIntent.getForegroundService(context, 0, 296 intent, PendingIntent.FLAG_IMMUTABLE); 297 sPendingIntent.put(targetPackage, pendingIntent); 298 } 299 doSendFgslPendingIntent(Context context, Intent commandIntent)300 private void doSendFgslPendingIntent(Context context, Intent commandIntent) { 301 final String targetPackage = getTargetPackage(commandIntent); 302 try { 303 ((PendingIntent) sPendingIntent.remove(targetPackage)).send(); 304 } catch (PendingIntent.CanceledException e) { 305 Log.e(TAG, "Caugtht exception:", e); 306 } 307 } 308 doStartChildProcess(Context context, Intent intent)309 private void doStartChildProcess(Context context, Intent intent) { 310 final Bundle extras = intent.getExtras(); 311 final IBinder callback = extras.getBinder(EXTRA_CALLBACK); 312 final String[] cmdline = extras.getStringArray(EXTRA_CHILD_CMDLINE); 313 final Parcel data = Parcel.obtain(); 314 final Parcel reply = Parcel.obtain(); 315 316 try { 317 sChildProcess = Runtime.getRuntime().exec(cmdline); 318 if (sChildProcess != null) { 319 Log.i(TAG, "Forked child: " + sChildProcess); 320 callback.transact(RESULT_CHILD_PROCESS_STARTED, data, reply, 0); 321 } // else the remote will fail with timeout 322 } catch (Exception e) { 323 Log.e(TAG, "Unable to execute command", e); 324 sChildProcess = null; 325 } finally { 326 data.recycle(); 327 reply.recycle(); 328 } 329 } 330 doStopChildProcess(Context context, Intent intent)331 private void doStopChildProcess(Context context, Intent intent) { 332 final Bundle extras = intent.getExtras(); 333 final IBinder callback = extras.getBinder(EXTRA_CALLBACK); 334 final long timeout = extras.getLong(EXTRA_TIMEOUT); 335 waitForChildProcessGone(true, callback, RESULT_CHILD_PROCESS_STOPPED, timeout); 336 } 337 doWaitForChildProcessGone(Context context, Intent intent)338 private void doWaitForChildProcessGone(Context context, Intent intent) { 339 final Bundle extras = intent.getExtras(); 340 final IBinder callback = extras.getBinder(EXTRA_CALLBACK); 341 final long timeout = extras.getLong(EXTRA_TIMEOUT); 342 waitForChildProcessGone(false, callback, RESULT_CHILD_PROCESS_GONE, timeout); 343 } 344 waitForChildProcessGone(final boolean destroy, final IBinder callback, final int transactionCode, final long timeout)345 private static synchronized void waitForChildProcessGone(final boolean destroy, 346 final IBinder callback, final int transactionCode, final long timeout) { 347 if (destroy) { 348 sChildProcess.destroy(); 349 } 350 new Thread(() -> { 351 final Parcel data = Parcel.obtain(); 352 final Parcel reply = Parcel.obtain(); 353 try { 354 if (sChildProcess != null && sChildProcess.isAlive()) { 355 final boolean exit = sChildProcess.waitFor(timeout, TimeUnit.MILLISECONDS); 356 if (exit) { 357 Log.i(TAG, "Child process died: " + sChildProcess); 358 callback.transact(transactionCode, data, reply, 0); 359 } else { 360 Log.w(TAG, "Child process is still alive: " + sChildProcess); 361 } 362 } else { 363 callback.transact(transactionCode, data, reply, 0); 364 } 365 } catch (Exception e) { 366 Log.e(TAG, "Error", e); 367 } finally { 368 data.recycle(); 369 reply.recycle(); 370 } 371 }).start(); 372 } 373 getTargetPackage(Intent intent)374 private String getTargetPackage(Intent intent) { 375 return intent.getStringExtra(EXTRA_TARGET_PACKAGE); 376 } 377 getFlags(Intent intent)378 private int getFlags(Intent intent) { 379 return intent.getIntExtra(EXTRA_FLAGS, 0); 380 } 381 sendCommand(Context context, int command, String sourcePackage, String targetPackage, int flags, Bundle extras)382 public static void sendCommand(Context context, int command, String sourcePackage, 383 String targetPackage, int flags, Bundle extras) { 384 final Intent intent = makeIntent(command, sourcePackage, targetPackage, flags, extras); 385 Log.d(TAG, "Sending broadcast " + intent); 386 context.sendOrderedBroadcast(intent, null); 387 } 388 sendCommandWithResultReceiver(Context context, int command, String sourcePackage, String targetPackage, int flags, Bundle extras, BroadcastReceiver resultReceiver)389 public static void sendCommandWithResultReceiver(Context context, int command, 390 String sourcePackage, String targetPackage, int flags, Bundle extras, 391 BroadcastReceiver resultReceiver) { 392 final Intent intent = makeIntent(command, sourcePackage, targetPackage, flags, extras); 393 Log.d(TAG, "Sending broadcast with result receiver " + intent); 394 context.sendOrderedBroadcast(intent, null, resultReceiver, null, 395 Activity.RESULT_OK, null, null); 396 } 397 sendCommandWithBroadcastOptions(Context context, int command, String sourcePackage, String targetPackage, int flags, Bundle extras, Bundle broadcastOptions)398 public static void sendCommandWithBroadcastOptions(Context context, int command, 399 String sourcePackage, String targetPackage, int flags, Bundle extras, 400 Bundle broadcastOptions) { 401 final Intent intent = makeIntent(command, sourcePackage, targetPackage, flags, extras); 402 Log.d(TAG, "Sending broadcast with BroadcastOptions " + intent); 403 context.sendOrderedBroadcast(intent, null, broadcastOptions, null, null, 0, null, null); 404 } 405 makeIntent(int command, String sourcePackage, String targetPackage, int flags, Bundle extras)406 private static Intent makeIntent(int command, String sourcePackage, 407 String targetPackage, int flags, Bundle extras) { 408 Intent intent = new Intent(); 409 if (command == COMMAND_BIND_SERVICE || command == COMMAND_START_FOREGROUND_SERVICE) { 410 intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); 411 } 412 intent.setComponent(new ComponentName(sourcePackage, "android.app.stubs.CommandReceiver")); 413 intent.putExtra(EXTRA_COMMAND, command); 414 intent.putExtra(EXTRA_FLAGS, flags); 415 intent.putExtra(EXTRA_TARGET_PACKAGE, targetPackage); 416 if (extras != null) { 417 intent.putExtras(extras); 418 } 419 return intent; 420 } 421 addServiceConnection(final String packageName)422 private ServiceConnection addServiceConnection(final String packageName) { 423 ServiceConnection connection = new ServiceConnection() { 424 @Override 425 public void onServiceConnected(ComponentName name, IBinder service) { 426 } 427 428 @Override 429 public void onServiceDisconnected(ComponentName name) { 430 } 431 }; 432 sServiceMap.put(packageName, connection); 433 return connection; 434 } 435 } 436