• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.IActivityManager;
23 import android.app.PendingIntent;
24 import android.content.BroadcastReceiver;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.ServiceConnection;
29 import android.content.pm.PackageManager;
30 import android.media.session.MediaSession;
31 import android.os.Bundle;
32 import android.os.IBinder;
33 import android.os.Parcel;
34 import android.os.RemoteCallback;
35 import android.os.RemoteException;
36 import android.text.TextUtils;
37 import android.util.ArrayMap;
38 import android.util.Log;
39 
40 import java.util.concurrent.TimeUnit;
41 
42 public class CommandReceiver extends BroadcastReceiver {
43 
44     private static final String TAG = "CommandReceiver";
45 
46     // Requires flags and targetPackage
47     public static final int COMMAND_BIND_SERVICE = 1;
48     // Requires targetPackage
49     public static final int COMMAND_UNBIND_SERVICE = 2;
50     public static final int COMMAND_START_FOREGROUND_SERVICE = 3;
51     public static final int COMMAND_STOP_FOREGROUND_SERVICE = 4;
52     public static final int COMMAND_START_FOREGROUND_SERVICE_LOCATION = 5;
53     public static final int COMMAND_STOP_FOREGROUND_SERVICE_LOCATION = 6;
54     public static final int COMMAND_START_ALERT_SERVICE = 7;
55     public static final int COMMAND_STOP_ALERT_SERVICE = 8;
56     public static final int COMMAND_SELF_INDUCED_ANR = 9;
57     public static final int COMMAND_START_ACTIVITY = 10;
58     public static final int COMMAND_STOP_ACTIVITY = 11;
59     public static final int COMMAND_CREATE_FGSL_PENDING_INTENT = 12;
60     public static final int COMMAND_SEND_FGSL_PENDING_INTENT = 13;
61     public static final int COMMAND_BIND_FOREGROUND_SERVICE = 14;
62     public static final int COMMAND_START_CHILD_PROCESS = 15;
63     public static final int COMMAND_STOP_CHILD_PROCESS = 16;
64     public static final int COMMAND_WAIT_FOR_CHILD_PROCESS_GONE = 17;
65     public static final int COMMAND_START_SERVICE = 18;
66     public static final int COMMAND_STOP_SERVICE = 19;
67     public static final int COMMAND_START_FOREGROUND_SERVICE_STICKY = 20;
68     public static final int COMMAND_STOP_FOREGROUND_SERVICE_STICKY = 21;
69     public static final int COMMAND_EMPTY = 22;
70     public static final int COMMAND_START_FOREGROUND_SERVICE_SPOOF_PACKAGE_NAME = 23;
71     public static final int COMMAND_CREATE_ACTIVE_MEDIA_SESSION = 24;
72 
73     public static final int RESULT_CHILD_PROCESS_STARTED = IBinder.FIRST_CALL_TRANSACTION;
74     public static final int RESULT_CHILD_PROCESS_STOPPED = IBinder.FIRST_CALL_TRANSACTION + 1;
75     public static final int RESULT_CHILD_PROCESS_GONE = IBinder.FIRST_CALL_TRANSACTION + 2;
76 
77     public static final String EXTRA_COMMAND = "android.app.stubs.extra.COMMAND";
78     public static final String EXTRA_TARGET_PACKAGE = "android.app.stubs.extra.TARGET_PACKAGE";
79     public static final String EXTRA_FLAGS = "android.app.stubs.extra.FLAGS";
80     public static final String EXTRA_CALLBACK = "android.app.stubs.extra.callback";
81     public static final String EXTRA_CHILD_CMDLINE = "android.app.stubs.extra.child_cmdline";
82     public static final String EXTRA_TIMEOUT = "android.app.stubs.extra.child_cmdline";
83     public static final String EXTRA_MESSENGER = "android.app.stubs.extra.EXTRA_MESSENGER";
84 
85     public static final String SERVICE_NAME = "android.app.stubs.LocalService";
86     public static final String FG_SERVICE_NAME = "android.app.stubs.LocalForegroundService";
87     public static final String FG_LOCATION_SERVICE_NAME =
88             "android.app.stubs.LocalForegroundServiceLocation";
89     public static final String FG_STICKY_SERVICE_NAME =
90             "android.app.stubs.LocalForegroundServiceSticky";
91 
92     public static final String ACTIVITY_NAME = "android.app.stubs.SimpleActivity";
93 
94     private static ArrayMap<String,ServiceConnection> sServiceMap = new ArrayMap<>();
95 
96     // Map a packageName to a Intent that starts an Activity.
97     private static ArrayMap<String, Intent> sActivityIntent = new ArrayMap<>();
98 
99     // Map a packageName to a PendingIntent.
100     private static ArrayMap<String, PendingIntent> sPendingIntent = new ArrayMap<>();
101 
102     /** The child process, started via {@link #COMMAND_START_CHILD_PROCESS} */
103     private static Process sChildProcess;
104 
105     private static MediaSession mMediaSession = null;
106 
107     /**
108      * Handle the different types of binding/unbinding requests.
109      * @param context The Context in which the receiver is running.
110      * @param intent The Intent being received.
111      */
112     @Override
onReceive(Context context, Intent intent)113     public void onReceive(Context context, Intent intent) {
114         // Use the application context as the receiver context could be restricted.
115         context = context.getApplicationContext();
116         int command = intent.getIntExtra(EXTRA_COMMAND, -1);
117         Log.d(TAG + "_" + context.getPackageName(), "Got command " + command + ", intent="
118                 + intent);
119         switch (command) {
120             case COMMAND_BIND_SERVICE:
121                 doBindService(context, intent, SERVICE_NAME);
122                 break;
123             case COMMAND_UNBIND_SERVICE:
124                 doUnbindService(context, intent);
125                 break;
126             case COMMAND_START_FOREGROUND_SERVICE:
127                 doStartForegroundService(context, intent);
128                 break;
129             case COMMAND_START_SERVICE:
130                 doStartService(context, intent);
131                 break;
132             case COMMAND_STOP_FOREGROUND_SERVICE:
133             case COMMAND_STOP_SERVICE:
134                 doStopService(context, intent, FG_SERVICE_NAME);
135                 break;
136             case COMMAND_START_FOREGROUND_SERVICE_LOCATION:
137                 doStartForegroundServiceWithType(context, intent);
138                 break;
139             case COMMAND_STOP_FOREGROUND_SERVICE_LOCATION:
140                 doStopService(context, intent, FG_LOCATION_SERVICE_NAME);
141                 break;
142             case COMMAND_START_FOREGROUND_SERVICE_STICKY:
143                 doStartForegroundServiceSticky(context, intent);
144                 break;
145             case COMMAND_STOP_FOREGROUND_SERVICE_STICKY:
146                 doStopService(context, intent, FG_STICKY_SERVICE_NAME);
147                 break;
148             case COMMAND_START_ALERT_SERVICE:
149                 doStartAlertService(context);
150                 break;
151             case COMMAND_STOP_ALERT_SERVICE:
152                 doStopAlertService(context);
153                 break;
154             case COMMAND_SELF_INDUCED_ANR:
155                 doSelfInducedAnr(context);
156                 break;
157             case COMMAND_START_ACTIVITY:
158                 doStartActivity(context, intent);
159                 break;
160             case COMMAND_STOP_ACTIVITY:
161                 doStopActivity(context, intent);
162                 break;
163             case COMMAND_CREATE_FGSL_PENDING_INTENT:
164                 doCreateFgslPendingIntent(context, intent);
165                 break;
166             case COMMAND_SEND_FGSL_PENDING_INTENT:
167                 doSendFgslPendingIntent(context, intent);
168                 break;
169             case COMMAND_BIND_FOREGROUND_SERVICE:
170                 doBindService(context, intent, FG_LOCATION_SERVICE_NAME);
171                 break;
172             case COMMAND_START_CHILD_PROCESS:
173                 doStartChildProcess(context, intent);
174                 break;
175             case COMMAND_STOP_CHILD_PROCESS:
176                 doStopChildProcess(context, intent);
177                 break;
178             case COMMAND_WAIT_FOR_CHILD_PROCESS_GONE:
179                 doWaitForChildProcessGone(context, intent);
180                 break;
181             case COMMAND_EMPTY:
182                 break;
183             case COMMAND_START_FOREGROUND_SERVICE_SPOOF_PACKAGE_NAME:
184                 doStartForegroundServiceSpoofPackageName(context, intent);
185                 break;
186             case COMMAND_CREATE_ACTIVE_MEDIA_SESSION:
187                 doStartMediaPlayback(context, intent.getParcelableExtra(
188                         Intent.EXTRA_REMOTE_CALLBACK, RemoteCallback.class));
189                 break;
190         }
191     }
192 
doBindService(Context context, Intent commandIntent, String serviceName)193     private void doBindService(Context context, Intent commandIntent, String serviceName) {
194         String targetPackage = getTargetPackage(commandIntent);
195         int flags = getFlags(commandIntent);
196 
197         Intent bindIntent = new Intent();
198         bindIntent.setComponent(new ComponentName(targetPackage, serviceName));
199 
200         ServiceConnection connection = addServiceConnection(targetPackage);
201 
202         context.bindService(bindIntent, connection, flags | Context.BIND_AUTO_CREATE);
203     }
204 
doUnbindService(Context context, Intent commandIntent)205     private void doUnbindService(Context context, Intent commandIntent) {
206         String targetPackage = getTargetPackage(commandIntent);
207         context.unbindService(sServiceMap.remove(targetPackage));
208     }
209 
doStartForegroundService(Context context, Intent commandIntent)210     private void doStartForegroundService(Context context, Intent commandIntent) {
211         String targetPackage = getTargetPackage(commandIntent);
212         Intent fgsIntent = new Intent();
213         fgsIntent.putExtras(commandIntent);
214         fgsIntent.setComponent(new ComponentName(targetPackage, FG_SERVICE_NAME));
215         int command = LocalForegroundService.COMMAND_START_FOREGROUND;
216         fgsIntent.putExtras(LocalForegroundService.newCommand(command));
217         try {
218             context.startForegroundService(fgsIntent);
219         } catch (ForegroundServiceStartNotAllowedException e) {
220             Log.d(TAG, "startForegroundService gets an "
221                     + " ForegroundServiceStartNotAllowedException", e);
222         }
223     }
224 
doStartService(Context context, Intent commandIntent)225     private void doStartService(Context context, Intent commandIntent) {
226         String targetPackage = getTargetPackage(commandIntent);
227         Intent fgsIntent = new Intent();
228         fgsIntent.putExtras(commandIntent);
229         fgsIntent.setComponent(new ComponentName(targetPackage, FG_SERVICE_NAME));
230         context.startService(fgsIntent);
231     }
232 
doStartForegroundServiceWithType(Context context, Intent commandIntent)233     private void doStartForegroundServiceWithType(Context context, Intent commandIntent) {
234         String targetPackage = getTargetPackage(commandIntent);
235         Intent fgsIntent = new Intent();
236         fgsIntent.putExtras(commandIntent); // include the fg service type if any.
237         fgsIntent.setComponent(new ComponentName(targetPackage, FG_LOCATION_SERVICE_NAME));
238         int command = LocalForegroundServiceLocation.COMMAND_START_FOREGROUND_WITH_TYPE;
239         fgsIntent.putExtras(LocalForegroundService.newCommand(command));
240         try {
241             context.startForegroundService(fgsIntent);
242         } catch (ForegroundServiceStartNotAllowedException e) {
243             Log.d(TAG, "startForegroundService gets an "
244                     + "ForegroundServiceStartNotAllowedException", e);
245         }
246     }
247 
doStartForegroundServiceSticky(Context context, Intent commandIntent)248     private void doStartForegroundServiceSticky(Context context, Intent commandIntent) {
249         String targetPackage = getTargetPackage(commandIntent);
250         Intent fgsIntent = new Intent();
251         fgsIntent.putExtras(commandIntent);
252         fgsIntent.setComponent(new ComponentName(targetPackage, FG_STICKY_SERVICE_NAME));
253         int command = LocalForegroundService.COMMAND_START_FOREGROUND;
254         fgsIntent.putExtras(LocalForegroundService.newCommand(command));
255         try {
256             context.startForegroundService(fgsIntent);
257         } catch (ForegroundServiceStartNotAllowedException e) {
258             Log.d(TAG, "startForegroundService gets an "
259                     + "ForegroundServiceStartNotAllowedException", e);
260         }
261     }
262 
doStopService(Context context, Intent commandIntent, String serviceName)263     private void doStopService(Context context, Intent commandIntent,
264             String serviceName) {
265         String targetPackage = getTargetPackage(commandIntent);
266         Intent fgsIntent = new Intent();
267         fgsIntent.setComponent(new ComponentName(targetPackage, serviceName));
268         context.stopService(fgsIntent);
269     }
270 
doStartAlertService(Context context)271     private void doStartAlertService(Context context) {
272         Intent intent = new Intent(context, LocalAlertService.class);
273         intent.setAction(LocalAlertService.COMMAND_SHOW_ALERT);
274         context.startService(intent);
275     }
276 
doStopAlertService(Context context)277     private void doStopAlertService(Context context) {
278         Intent intent = new Intent(context, LocalAlertService.class);
279         intent.setAction(LocalAlertService.COMMAND_HIDE_ALERT);
280         context.startService(intent);
281     }
282 
doSelfInducedAnr(Context context)283     private void doSelfInducedAnr(Context context) {
284         ActivityManager am = context.getSystemService(ActivityManager.class);
285         am.appNotResponding("CTS - self induced");
286     }
287 
doStartActivity(Context context, Intent commandIntent)288     private void doStartActivity(Context context, Intent commandIntent) {
289         String targetPackage = getTargetPackage(commandIntent);
290         Intent activityIntent = new Intent(Intent.ACTION_MAIN);
291         sActivityIntent.put(targetPackage, activityIntent);
292         activityIntent.putExtras(commandIntent);
293         activityIntent.setComponent(new ComponentName(targetPackage, ACTIVITY_NAME));
294         activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
295         context.startActivity(activityIntent);
296     }
297 
doStopActivity(Context context, Intent commandIntent)298     private void doStopActivity(Context context, Intent commandIntent) {
299         String targetPackage = getTargetPackage(commandIntent);
300         Intent activityIntent = sActivityIntent.remove(targetPackage);
301         activityIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
302         activityIntent.putExtra("finish", true);
303         context.startActivity(activityIntent);
304     }
305 
doCreateFgslPendingIntent(Context context, Intent commandIntent)306     private void doCreateFgslPendingIntent(Context context, Intent commandIntent) {
307         final String targetPackage = getTargetPackage(commandIntent);
308         final Intent intent = new Intent().setComponent(
309                 new ComponentName(targetPackage, FG_LOCATION_SERVICE_NAME));
310         int command = LocalForegroundServiceLocation.COMMAND_START_FOREGROUND_WITH_TYPE;
311         intent.putExtras(LocalForegroundService.newCommand(command));
312         final PendingIntent pendingIntent = PendingIntent.getForegroundService(context, 0,
313                 intent, PendingIntent.FLAG_IMMUTABLE);
314         sPendingIntent.put(targetPackage, pendingIntent);
315     }
316 
doSendFgslPendingIntent(Context context, Intent commandIntent)317     private void doSendFgslPendingIntent(Context context, Intent commandIntent) {
318         final String targetPackage = getTargetPackage(commandIntent);
319         try {
320             ((PendingIntent) sPendingIntent.remove(targetPackage)).send();
321         } catch (PendingIntent.CanceledException e) {
322             Log.e(TAG, "Caugtht exception:", e);
323         }
324     }
325 
doStartChildProcess(Context context, Intent intent)326     private void doStartChildProcess(Context context, Intent intent) {
327         final Bundle extras = intent.getExtras();
328         final IBinder callback = extras.getBinder(EXTRA_CALLBACK);
329         final String[] cmdline = extras.getStringArray(EXTRA_CHILD_CMDLINE);
330         final Parcel data = Parcel.obtain();
331         final Parcel reply = Parcel.obtain();
332 
333         try {
334             sChildProcess = Runtime.getRuntime().exec(cmdline);
335             if (sChildProcess != null) {
336                 Log.i(TAG, "Forked child: " + sChildProcess);
337                 callback.transact(RESULT_CHILD_PROCESS_STARTED, data, reply, 0);
338             } // else the remote will fail with timeout
339         } catch (Exception e) {
340             Log.e(TAG, "Unable to execute command", e);
341             sChildProcess = null;
342         } finally {
343             data.recycle();
344             reply.recycle();
345         }
346     }
347 
doStopChildProcess(Context context, Intent intent)348     private void doStopChildProcess(Context context, Intent intent) {
349         final Bundle extras = intent.getExtras();
350         final IBinder callback = extras.getBinder(EXTRA_CALLBACK);
351         final long timeout = extras.getLong(EXTRA_TIMEOUT);
352         waitForChildProcessGone(true, callback, RESULT_CHILD_PROCESS_STOPPED, timeout);
353     }
354 
doWaitForChildProcessGone(Context context, Intent intent)355     private void doWaitForChildProcessGone(Context context, Intent intent) {
356         final Bundle extras = intent.getExtras();
357         final IBinder callback = extras.getBinder(EXTRA_CALLBACK);
358         final long timeout = extras.getLong(EXTRA_TIMEOUT);
359         waitForChildProcessGone(false, callback, RESULT_CHILD_PROCESS_GONE, timeout);
360     }
361 
waitForChildProcessGone(final boolean destroy, final IBinder callback, final int transactionCode, final long timeout)362     private static synchronized void waitForChildProcessGone(final boolean destroy,
363             final IBinder callback, final int transactionCode, final long timeout) {
364         if (destroy) {
365             sChildProcess.destroy();
366         }
367         new Thread(() -> {
368             final Parcel data = Parcel.obtain();
369             final Parcel reply = Parcel.obtain();
370             try {
371                 if (sChildProcess != null && sChildProcess.isAlive()) {
372                     final boolean exit = sChildProcess.waitFor(timeout, TimeUnit.MILLISECONDS);
373                     if (exit) {
374                         Log.i(TAG, "Child process died: " + sChildProcess);
375                         callback.transact(transactionCode, data, reply, 0);
376                     } else {
377                         Log.w(TAG, "Child process is still alive: " + sChildProcess);
378                     }
379                 } else {
380                     callback.transact(transactionCode, data, reply, 0);
381                 }
382             } catch (Exception e) {
383                 Log.e(TAG, "Error", e);
384             } finally {
385                 data.recycle();
386                 reply.recycle();
387             }
388         }).start();
389     }
390 
391     /**
392      * Directly call IActivityManager.startService() using a spoofed packageName which is known to
393      * be allowlisted by Android framework to be able to start foreground service
394      * from the background. Framework will disallow the foreground service to start from the
395      * background and a ForegroundServiceStartNotAllowedException will be caught.
396      * @param context
397      * @param commandIntent
398      */
doStartForegroundServiceSpoofPackageName(Context context, Intent commandIntent)399     private void doStartForegroundServiceSpoofPackageName(Context context, Intent commandIntent) {
400         String targetPackage = getTargetPackage(commandIntent);
401         Intent fgsIntent = new Intent();
402         fgsIntent.putExtras(commandIntent);
403         fgsIntent.setComponent(new ComponentName(targetPackage, FG_SERVICE_NAME));
404         int command = LocalForegroundService.COMMAND_START_FOREGROUND;
405         fgsIntent.putExtras(LocalForegroundService.newCommand(command));
406         try {
407             final PackageManager pm = context.getPackageManager();
408             String spoofPackageName = pm.getAttentionServicePackageName();
409             if (TextUtils.isEmpty(spoofPackageName)) {
410                 Log.d(TAG, "getAttentionServicePackageName() returns empty");
411                 spoofPackageName = pm.getSystemCaptionsServicePackageName();
412             }
413             if (TextUtils.isEmpty(spoofPackageName)) {
414                 Log.d(TAG, "getSystemCaptionsServicePackageName() returns empty");
415                 spoofPackageName = "android";
416             }
417             Log.d(TAG, "spoofPackageName: " + spoofPackageName);
418             final IBinder activityProxy = android.os.ServiceManager.getService("activity");
419             // Call IActivityManager.startService() directly using a spoofed packageName.
420             IActivityManager.Stub.asInterface(activityProxy).startService(
421                     context.getIApplicationThread(),
422                     fgsIntent,
423                     null,
424                     true,
425                     spoofPackageName,
426                     null,
427                     android.os.Process.myUserHandle().getIdentifier()
428             );
429         } catch (ForegroundServiceStartNotAllowedException e) {
430             Log.d(TAG, "startForegroundService gets an "
431                     + " ForegroundServiceStartNotAllowedException", e);
432         } catch (LinkageError e) {
433             // IActivityManager.startService() is a hidden API, access hidden API could get
434             // LinkageError, consider the test as pass if we get LinkageError.
435             Log.d(TAG, "startForegroundService gets an LinkageError", e);
436         } catch (RemoteException e) {
437             Log.d(TAG, "startForegroundService gets an RemoteException", e);
438         }
439     }
440 
doStartMediaPlayback(Context context, RemoteCallback callback)441     private void doStartMediaPlayback(Context context, RemoteCallback callback) {
442         mMediaSession = new MediaSession(context, TAG);
443         mMediaSession.setCallback(new MediaSession.Callback() {
444             @Override
445             public void onPlay() {
446                 super.onPlay();
447                 final Intent fgsIntent = new Intent(context, LocalForegroundService.class);
448                 fgsIntent.putExtras(LocalForegroundService.newCommand(
449                         LocalForegroundService.COMMAND_START_FOREGROUND));
450                 try {
451                     context.startForegroundService(fgsIntent);
452                 } catch (ForegroundServiceStartNotAllowedException e) {
453                     Log.e(TAG, "Error while trying to start an FGS", e);
454                 }
455             }
456 
457             @Override
458             public void onPause() {
459                 super.onPause();
460                 final Intent intent = new Intent(context, LocalForegroundService.class);
461                 intent.putExtras(LocalForegroundService.newCommand(
462                         LocalForegroundService.COMMAND_STOP_FOREGROUND_DONT_REMOVE_NOTIFICATION));
463                 context.startService(intent);
464             }
465 
466             @Override
467             public void onStop() {
468                 super.onStop();
469                 final Intent intent = new Intent(context, LocalForegroundService.class);
470                 context.stopService(intent);
471                 mMediaSession.release();
472             }
473         });
474         mMediaSession.setActive(true);
475 
476         callback.sendResult(null);
477     }
478 
getTargetPackage(Intent intent)479     private String getTargetPackage(Intent intent) {
480         return intent.getStringExtra(EXTRA_TARGET_PACKAGE);
481     }
482 
getFlags(Intent intent)483     private int getFlags(Intent intent) {
484         return intent.getIntExtra(EXTRA_FLAGS, 0);
485     }
486 
sendCommand(Context context, int command, String sourcePackage, String targetPackage, int flags, Bundle extras)487     public static void sendCommand(Context context, int command, String sourcePackage,
488             String targetPackage, int flags, Bundle extras) {
489         final Intent intent = makeIntent(command, sourcePackage, targetPackage, flags, extras);
490         Log.d(TAG, "Sending broadcast " + intent);
491         context.sendOrderedBroadcast(intent, null);
492     }
493 
sendCommandWithResultReceiver(Context context, int command, String sourcePackage, String targetPackage, int flags, Bundle extras, BroadcastReceiver resultReceiver)494     public static void sendCommandWithResultReceiver(Context context, int command,
495             String sourcePackage, String targetPackage, int flags, Bundle extras,
496             BroadcastReceiver resultReceiver) {
497         final Intent intent = makeIntent(command, sourcePackage, targetPackage, flags, extras);
498         Log.d(TAG, "Sending broadcast with result receiver " + intent);
499         context.sendOrderedBroadcast(intent, null, resultReceiver, null,
500                 Activity.RESULT_OK, null, null);
501     }
502 
sendCommandWithBroadcastOptions(Context context, int command, String sourcePackage, String targetPackage, int flags, Bundle extras, Bundle broadcastOptions)503     public static void sendCommandWithBroadcastOptions(Context context, int command,
504             String sourcePackage, String targetPackage, int flags, Bundle extras,
505             Bundle broadcastOptions) {
506         final Intent intent = makeIntent(command, sourcePackage, targetPackage, flags, extras);
507         Log.d(TAG, "Sending broadcast with BroadcastOptions " + intent);
508         context.sendOrderedBroadcast(intent, null, broadcastOptions, null, null, 0, null, null);
509     }
510 
makeIntent(int command, String sourcePackage, String targetPackage, int flags, Bundle extras)511     private static Intent makeIntent(int command, String sourcePackage,
512             String targetPackage, int flags, Bundle extras) {
513         Intent intent = new Intent();
514         if (command == COMMAND_BIND_SERVICE || command == COMMAND_START_FOREGROUND_SERVICE
515                 || command == COMMAND_STOP_FOREGROUND_SERVICE || command == COMMAND_START_ACTIVITY
516                 || command == COMMAND_START_FOREGROUND_SERVICE_LOCATION || command == COMMAND_UNBIND_SERVICE) {
517             intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
518         }
519         intent.setComponent(new ComponentName(sourcePackage, "android.app.stubs.CommandReceiver"));
520         intent.putExtra(EXTRA_COMMAND, command);
521         intent.putExtra(EXTRA_FLAGS, flags);
522         intent.putExtra(EXTRA_TARGET_PACKAGE, targetPackage);
523         if (extras != null) {
524             intent.putExtras(extras);
525         }
526         return intent;
527     }
528 
addServiceConnection(final String packageName)529     private ServiceConnection addServiceConnection(final String packageName) {
530         ServiceConnection connection = new ServiceConnection() {
531             @Override
532             public void onServiceConnected(ComponentName name, IBinder service) {
533             }
534 
535             @Override
536             public void onServiceDisconnected(ComponentName name) {
537             }
538         };
539         sServiceMap.put(packageName, connection);
540         return connection;
541     }
542 }
543