• 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 static android.content.ContentResolver.SCHEME_CONTENT;
20 
21 import android.app.Activity;
22 import android.app.ActivityManager;
23 import android.app.BroadcastOptions;
24 import android.app.ForegroundServiceStartNotAllowedException;
25 import android.app.IActivityManager;
26 import android.app.Notification;
27 import android.app.NotificationChannel;
28 import android.app.NotificationManager;
29 import android.app.PendingIntent;
30 import android.content.BroadcastReceiver;
31 import android.content.ComponentName;
32 import android.content.Context;
33 import android.content.IContentProvider;
34 import android.content.Intent;
35 import android.content.IntentFilter;
36 import android.content.ServiceConnection;
37 import android.content.pm.PackageManager;
38 import android.media.session.MediaSession;
39 import android.media.session.PlaybackState;
40 import android.net.Uri;
41 import android.os.Bundle;
42 import android.os.IBinder;
43 import android.os.Parcel;
44 import android.os.RemoteCallback;
45 import android.os.RemoteException;
46 import android.text.TextUtils;
47 import android.util.ArrayMap;
48 import android.util.Log;
49 
50 import java.util.concurrent.TimeUnit;
51 
52 public class CommandReceiver extends BroadcastReceiver {
53 
54     private static final String TAG = "CommandReceiver";
55 
56     // Requires flags and targetPackage
57     public static final int COMMAND_BIND_SERVICE = 1;
58     // Requires targetPackage
59     public static final int COMMAND_UNBIND_SERVICE = 2;
60     public static final int COMMAND_START_FOREGROUND_SERVICE = 3;
61     public static final int COMMAND_STOP_FOREGROUND_SERVICE = 4;
62     public static final int COMMAND_START_FOREGROUND_SERVICE_LOCATION = 5;
63     public static final int COMMAND_STOP_FOREGROUND_SERVICE_LOCATION = 6;
64 
65     public static final int COMMAND_START_ALERT_SERVICE = 7;
66     public static final int COMMAND_STOP_ALERT_SERVICE = 8;
67     public static final int COMMAND_SELF_INDUCED_ANR = 9;
68     public static final int COMMAND_START_ACTIVITY = 10;
69     public static final int COMMAND_STOP_ACTIVITY = 11;
70     public static final int COMMAND_CREATE_FGSL_PENDING_INTENT = 12;
71     public static final int COMMAND_SEND_FGSL_PENDING_INTENT = 13;
72     public static final int COMMAND_BIND_FOREGROUND_SERVICE = 14;
73     public static final int COMMAND_START_CHILD_PROCESS = 15;
74     public static final int COMMAND_STOP_CHILD_PROCESS = 16;
75     public static final int COMMAND_WAIT_FOR_CHILD_PROCESS_GONE = 17;
76     public static final int COMMAND_START_SERVICE = 18;
77     public static final int COMMAND_STOP_SERVICE = 19;
78     public static final int COMMAND_START_FOREGROUND_SERVICE_STICKY = 20;
79     public static final int COMMAND_STOP_FOREGROUND_SERVICE_STICKY = 21;
80     public static final int COMMAND_EMPTY = 22;
81     public static final int COMMAND_START_FOREGROUND_SERVICE_SPOOF_PACKAGE_NAME = 23;
82     public static final int COMMAND_CREATE_ACTIVE_MEDIA_SESSION = 24;
83     public static final int COMMAND_CREATE_MEDIA_SESSION_FGS_DELEGATE = 25;
84     public static final int COMMAND_ACTIVATE_MEDIA_SESSION_FGS_DELEGATE = 26;
85     public static final int COMMAND_DEACTIVATE_MEDIA_SESSION_FGS_DELEGATE = 27;
86     public static final int COMMAND_RELEASE_MEDIA_SESSION_FGS_DELEGATE = 28;
87     public static final int COMMAND_SEND_STICKY_BROADCAST = 29;
88     public static final int COMMAND_SET_MEDIA_SESSION_TO_PLAYING = 30;
89     public static final int COMMAND_SET_MEDIA_SESSION_TO_PAUSED = 31;
90     public static final int COMMAND_SET_MEDIA_SESSION_TO_STOPPED = 32;
91     public static final int COMMAND_CREATE_MEDIA_NOTIFICATION = 33;
92     public static final int COMMAND_ACQUIRE_CONTENT_PROVIDER = 34;
93     public static final int COMMAND_RELEASE_CONTENT_PROVIDER = 35;
94     public static final int COMMAND_START_FOREGROUND_SERVICE_MEDIA = 36;
95     public static final int COMMAND_STOP_FOREGROUND_SERVICE_MEDIA = 37;
96     public static final int COMMAND_START_SERVICE_MEDIA = 38;
97 
98     public static final String KEY_PENDING_INTENT = "android.app.stubs.key.PENDING_INTENT";
99     public static final String KEY_STICKY_BROADCAST_FILTER =
100             "android.app.stubs.key.STICKY_BROADCAST_FILTER";
101 
102     public static final int RESULT_CHILD_PROCESS_STARTED = IBinder.FIRST_CALL_TRANSACTION;
103     public static final int RESULT_CHILD_PROCESS_STOPPED = IBinder.FIRST_CALL_TRANSACTION + 1;
104     public static final int RESULT_CHILD_PROCESS_GONE = IBinder.FIRST_CALL_TRANSACTION + 2;
105 
106     public static final String EXTRA_COMMAND = "android.app.stubs.extra.COMMAND";
107     public static final String EXTRA_TARGET_PACKAGE = "android.app.stubs.extra.TARGET_PACKAGE";
108     public static final String EXTRA_FLAGS = "android.app.stubs.extra.FLAGS";
109     public static final String EXTRA_CALLBACK = "android.app.stubs.extra.callback";
110     public static final String EXTRA_CHILD_CMDLINE = "android.app.stubs.extra.child_cmdline";
111     public static final String EXTRA_TIMEOUT = "android.app.stubs.extra.child_cmdline";
112     public static final String EXTRA_MESSENGER = "android.app.stubs.extra.EXTRA_MESSENGER";
113     public static final String EXTRA_URI = "android.app.stubs.extra.EXTRA_URI";
114 
115     public static final String SERVICE_NAME = "android.app.stubs.LocalService";
116     public static final String FG_SERVICE_NAME = "android.app.stubs.LocalForegroundService";
117     public static final String FG_LOCATION_SERVICE_NAME =
118             "android.app.stubs.LocalForegroundServiceLocation";
119     public static final String FG_STICKY_SERVICE_NAME =
120             "android.app.stubs.LocalForegroundServiceSticky";
121     public static final String FG_MEDIA_SERVICE_NAME =
122             "android.app.stubs.LocalForegroundServiceMedia";
123 
124     public static final String ACTIVITY_NAME = "android.app.stubs.SimpleActivity";
125 
126     private static ArrayMap<String,ServiceConnection> sServiceMap = new ArrayMap<>();
127 
128     // Map a packageName to a Intent that starts an Activity.
129     private static ArrayMap<String, Intent> sActivityIntent = new ArrayMap<>();
130 
131     // Map a packageName to a PendingIntent.
132     private static ArrayMap<String, PendingIntent> sPendingIntent = new ArrayMap<>();
133 
134     /** The child process, started via {@link #COMMAND_START_CHILD_PROCESS} */
135     private static Process sChildProcess;
136 
137     private static MediaSession mMediaSession = null;
138 
139     private String mNotificationChannelId;
140     private static final String NOTIFICATION_CHANNEL_ID = "com.example.android.media.channel";
141 
142     private int mNotificationId = 6003;
143 
144     private static ArrayMap<Uri, IContentProvider> sContentProviders = new ArrayMap<>();
145 
146     /**
147      * Handle the different types of binding/unbinding requests.
148      * @param context The Context in which the receiver is running.
149      * @param intent The Intent being received.
150      */
151     @Override
onReceive(Context context, Intent intent)152     public void onReceive(Context context, Intent intent) {
153         // Use the application context as the receiver context could be restricted.
154         context = context.getApplicationContext();
155         int command = intent.getIntExtra(EXTRA_COMMAND, -1);
156         Log.d(TAG + "_" + context.getPackageName(), "Got command " + command + ", intent="
157                 + intent);
158         Bundle resultExtras = null;
159         switch (command) {
160             case COMMAND_BIND_SERVICE:
161                 doBindService(context, intent, SERVICE_NAME);
162                 break;
163             case COMMAND_UNBIND_SERVICE:
164                 doUnbindService(context, intent);
165                 break;
166             case COMMAND_START_FOREGROUND_SERVICE:
167                 doStartForegroundService(context, intent);
168                 break;
169             case COMMAND_START_SERVICE:
170                 doStartService(context, intent);
171                 break;
172             case COMMAND_STOP_FOREGROUND_SERVICE:
173             case COMMAND_STOP_SERVICE:
174                 doStopService(context, intent, FG_SERVICE_NAME);
175                 break;
176             case COMMAND_START_FOREGROUND_SERVICE_LOCATION:
177                 doStartForegroundServiceWithType(context, intent);
178                 break;
179             case COMMAND_STOP_FOREGROUND_SERVICE_LOCATION:
180                 doStopService(context, intent, FG_LOCATION_SERVICE_NAME);
181                 break;
182             case COMMAND_START_FOREGROUND_SERVICE_STICKY:
183                 doStartForegroundServiceSticky(context, intent);
184                 break;
185             case COMMAND_STOP_FOREGROUND_SERVICE_STICKY:
186                 doStopService(context, intent, FG_STICKY_SERVICE_NAME);
187                 break;
188             case COMMAND_START_SERVICE_MEDIA:
189                 doStartServiceMedia(context, intent);
190                 break;
191             case COMMAND_START_FOREGROUND_SERVICE_MEDIA:
192                 doStartForegroundServiceMedia(context, intent);
193                 break;
194             case COMMAND_STOP_FOREGROUND_SERVICE_MEDIA:
195                 doStopService(context, intent, FG_MEDIA_SERVICE_NAME);
196                 break;
197             case COMMAND_START_ALERT_SERVICE:
198                 doStartAlertService(context);
199                 break;
200             case COMMAND_STOP_ALERT_SERVICE:
201                 doStopAlertService(context);
202                 break;
203             case COMMAND_SELF_INDUCED_ANR:
204                 doSelfInducedAnr(context);
205                 break;
206             case COMMAND_START_ACTIVITY:
207                 doStartActivity(context, intent);
208                 break;
209             case COMMAND_STOP_ACTIVITY:
210                 doStopActivity(context, intent);
211                 break;
212             case COMMAND_CREATE_FGSL_PENDING_INTENT:
213                 final PendingIntent pendingIntent = doCreateFgslPendingIntent(context, intent);
214                 resultExtras = new Bundle();
215                 resultExtras.putParcelable(KEY_PENDING_INTENT, pendingIntent);
216                 break;
217             case COMMAND_SEND_FGSL_PENDING_INTENT:
218                 doSendFgslPendingIntent(context, intent);
219                 break;
220             case COMMAND_BIND_FOREGROUND_SERVICE:
221                 doBindService(context, intent, FG_LOCATION_SERVICE_NAME);
222                 break;
223             case COMMAND_START_CHILD_PROCESS:
224                 doStartChildProcess(context, intent);
225                 break;
226             case COMMAND_STOP_CHILD_PROCESS:
227                 doStopChildProcess(context, intent);
228                 break;
229             case COMMAND_WAIT_FOR_CHILD_PROCESS_GONE:
230                 doWaitForChildProcessGone(context, intent);
231                 break;
232             case COMMAND_EMPTY:
233                 break;
234             case COMMAND_START_FOREGROUND_SERVICE_SPOOF_PACKAGE_NAME:
235                 doStartForegroundServiceSpoofPackageName(context, intent);
236                 break;
237             case COMMAND_CREATE_ACTIVE_MEDIA_SESSION:
238                 doStartMediaPlayback(context, intent.getParcelableExtra(
239                         Intent.EXTRA_REMOTE_CALLBACK, RemoteCallback.class));
240                 break;
241             case COMMAND_CREATE_MEDIA_SESSION_FGS_DELEGATE:
242                 doCreateMediaSession(
243                         context,
244                         intent.getParcelableExtra(
245                                 Intent.EXTRA_REMOTE_CALLBACK, RemoteCallback.class));
246                 break;
247             case COMMAND_ACTIVATE_MEDIA_SESSION_FGS_DELEGATE:
248                 doChangeMediaSessionActiveState(
249                         /* isActive= */ true,
250                         intent.getParcelableExtra(
251                                 Intent.EXTRA_REMOTE_CALLBACK, RemoteCallback.class));
252                 break;
253             case COMMAND_DEACTIVATE_MEDIA_SESSION_FGS_DELEGATE:
254                 doChangeMediaSessionActiveState(
255                         /* isActive= */ false,
256                         intent.getParcelableExtra(
257                                 Intent.EXTRA_REMOTE_CALLBACK, RemoteCallback.class));
258                 break;
259             case COMMAND_RELEASE_MEDIA_SESSION_FGS_DELEGATE:
260                 doReleaseMediaSession(
261                         intent.getParcelableExtra(
262                                 Intent.EXTRA_REMOTE_CALLBACK, RemoteCallback.class));
263                 break;
264             case COMMAND_SET_MEDIA_SESSION_TO_PLAYING:
265                 doSetMediaSessionPlaybackState(
266                         PlaybackState.STATE_PLAYING,
267                         intent.getParcelableExtra(
268                                 Intent.EXTRA_REMOTE_CALLBACK, RemoteCallback.class));
269                 break;
270             case COMMAND_SET_MEDIA_SESSION_TO_PAUSED:
271                 doSetMediaSessionPlaybackState(
272                         PlaybackState.STATE_PAUSED,
273                         intent.getParcelableExtra(
274                                 Intent.EXTRA_REMOTE_CALLBACK, RemoteCallback.class));
275                 break;
276             case COMMAND_SET_MEDIA_SESSION_TO_STOPPED:
277                 doSetMediaSessionPlaybackState(
278                         PlaybackState.STATE_STOPPED,
279                         intent.getParcelableExtra(
280                                 Intent.EXTRA_REMOTE_CALLBACK, RemoteCallback.class));
281                 break;
282             case COMMAND_CREATE_MEDIA_NOTIFICATION:
283                 doCreateMediaNotification(
284                         context,
285                         intent.getParcelableExtra(
286                                 Intent.EXTRA_REMOTE_CALLBACK, RemoteCallback.class));
287                 break;
288             case COMMAND_SEND_STICKY_BROADCAST:
289                 final IntentFilter intentFilter = doSendStickyBroadcast(context);
290                 resultExtras = new Bundle();
291                 if (intentFilter != null) {
292                     resultExtras.putParcelable(KEY_STICKY_BROADCAST_FILTER, intentFilter);
293                 }
294                 break;
295             case COMMAND_ACQUIRE_CONTENT_PROVIDER:
296                 doAcquireProvider(context, intent);
297                 break;
298             case COMMAND_RELEASE_CONTENT_PROVIDER:
299                 doReleaseProvider(context, intent);
300                 break;
301         }
302         if (resultExtras != null) {
303             setResultExtras(resultExtras);
304         }
305     }
306 
doBindService(Context context, Intent commandIntent, String serviceName)307     private void doBindService(Context context, Intent commandIntent, String serviceName) {
308         String targetPackage = getTargetPackage(commandIntent);
309         int flags = getFlags(commandIntent);
310 
311         Intent bindIntent = new Intent();
312         bindIntent.setComponent(new ComponentName(targetPackage, serviceName));
313 
314         ServiceConnection connection = addServiceConnection(targetPackage);
315 
316         context.bindService(bindIntent, connection, flags | Context.BIND_AUTO_CREATE);
317     }
318 
doUnbindService(Context context, Intent commandIntent)319     private void doUnbindService(Context context, Intent commandIntent) {
320         String targetPackage = getTargetPackage(commandIntent);
321         context.unbindService(sServiceMap.remove(targetPackage));
322     }
323 
doStartForegroundService(Context context, Intent commandIntent)324     private void doStartForegroundService(Context context, Intent commandIntent) {
325         String targetPackage = getTargetPackage(commandIntent);
326         Intent fgsIntent = new Intent();
327         fgsIntent.putExtras(commandIntent);
328         fgsIntent.setComponent(new ComponentName(targetPackage, FG_SERVICE_NAME));
329         int command = LocalForegroundService.COMMAND_START_FOREGROUND;
330         fgsIntent.putExtras(LocalForegroundService.newCommand(command));
331         try {
332             context.startForegroundService(fgsIntent);
333         } catch (ForegroundServiceStartNotAllowedException e) {
334             Log.d(TAG, "startForegroundService gets an "
335                     + " ForegroundServiceStartNotAllowedException", e);
336         }
337     }
338 
doStartService(Context context, Intent commandIntent)339     private void doStartService(Context context, Intent commandIntent) {
340         String targetPackage = getTargetPackage(commandIntent);
341         Intent fgsIntent = new Intent();
342         fgsIntent.putExtras(commandIntent);
343         fgsIntent.setComponent(new ComponentName(targetPackage, FG_SERVICE_NAME));
344         context.startService(fgsIntent);
345     }
346 
doStartForegroundServiceWithType(Context context, Intent commandIntent)347     private void doStartForegroundServiceWithType(Context context, Intent commandIntent) {
348         String targetPackage = getTargetPackage(commandIntent);
349         Intent fgsIntent = new Intent();
350         fgsIntent.putExtras(commandIntent); // include the fg service type if any.
351         fgsIntent.setComponent(new ComponentName(targetPackage, FG_LOCATION_SERVICE_NAME));
352         int command = LocalForegroundServiceLocation.COMMAND_START_FOREGROUND_WITH_TYPE;
353         fgsIntent.putExtras(LocalForegroundService.newCommand(command));
354         try {
355             context.startForegroundService(fgsIntent);
356         } catch (ForegroundServiceStartNotAllowedException e) {
357             Log.d(TAG, "startForegroundService gets an "
358                     + "ForegroundServiceStartNotAllowedException", e);
359         }
360     }
361 
doStartServiceMedia(Context context, Intent commandIntent)362     private void doStartServiceMedia(Context context, Intent commandIntent) {
363         String targetPackage = getTargetPackage(commandIntent);
364         Intent fgsIntent = new Intent();
365         fgsIntent.putExtras(commandIntent);
366         fgsIntent.setComponent(new ComponentName(targetPackage, FG_MEDIA_SERVICE_NAME));
367         context.startService(fgsIntent);
368     }
369 
doStartForegroundServiceMedia(Context context, Intent commandIntent)370     private void doStartForegroundServiceMedia(Context context, Intent commandIntent) {
371         String targetPackage = getTargetPackage(commandIntent);
372         Intent fgsIntent = new Intent();
373         fgsIntent.putExtras(commandIntent);
374         fgsIntent.setComponent(new ComponentName(targetPackage, FG_MEDIA_SERVICE_NAME));
375         int command = LocalForegroundServiceMedia.COMMAND_START_FOREGROUND_WITH_TYPE;
376         fgsIntent.putExtras(LocalForegroundService.newCommand(command));
377         try {
378             context.startForegroundService(fgsIntent);
379         } catch (ForegroundServiceStartNotAllowedException e) {
380             Log.d(TAG, "startForegroundService gets an "
381                     + "ForegroundServiceStartNotAllowedException", e);
382         }
383     }
384 
doStartForegroundServiceSticky(Context context, Intent commandIntent)385     private void doStartForegroundServiceSticky(Context context, Intent commandIntent) {
386         String targetPackage = getTargetPackage(commandIntent);
387         Intent fgsIntent = new Intent();
388         fgsIntent.putExtras(commandIntent);
389         fgsIntent.setComponent(new ComponentName(targetPackage, FG_STICKY_SERVICE_NAME));
390         int command = LocalForegroundService.COMMAND_START_FOREGROUND;
391         fgsIntent.putExtras(LocalForegroundService.newCommand(command));
392         try {
393             context.startForegroundService(fgsIntent);
394         } catch (ForegroundServiceStartNotAllowedException e) {
395             Log.d(TAG, "startForegroundService gets an "
396                     + "ForegroundServiceStartNotAllowedException", e);
397         }
398     }
399 
doStopService(Context context, Intent commandIntent, String serviceName)400     private void doStopService(Context context, Intent commandIntent,
401             String serviceName) {
402         String targetPackage = getTargetPackage(commandIntent);
403         Intent fgsIntent = new Intent();
404         fgsIntent.setComponent(new ComponentName(targetPackage, serviceName));
405         context.stopService(fgsIntent);
406     }
407 
doStartAlertService(Context context)408     private void doStartAlertService(Context context) {
409         Intent intent = new Intent(context, LocalAlertService.class);
410         intent.setAction(LocalAlertService.COMMAND_SHOW_ALERT);
411         context.startService(intent);
412     }
413 
doStopAlertService(Context context)414     private void doStopAlertService(Context context) {
415         Intent intent = new Intent(context, LocalAlertService.class);
416         intent.setAction(LocalAlertService.COMMAND_HIDE_ALERT);
417         context.startService(intent);
418     }
419 
doSelfInducedAnr(Context context)420     private void doSelfInducedAnr(Context context) {
421         ActivityManager am = context.getSystemService(ActivityManager.class);
422         am.appNotResponding("CTS - self induced");
423     }
424 
doStartActivity(Context context, Intent commandIntent)425     private void doStartActivity(Context context, Intent commandIntent) {
426         String targetPackage = getTargetPackage(commandIntent);
427         Intent activityIntent = new Intent(Intent.ACTION_MAIN);
428         sActivityIntent.put(targetPackage, activityIntent);
429         activityIntent.putExtras(commandIntent);
430         activityIntent.setComponent(new ComponentName(targetPackage, ACTIVITY_NAME));
431         activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
432         context.startActivity(activityIntent);
433     }
434 
doStopActivity(Context context, Intent commandIntent)435     private void doStopActivity(Context context, Intent commandIntent) {
436         String targetPackage = getTargetPackage(commandIntent);
437         Intent activityIntent = sActivityIntent.remove(targetPackage);
438         activityIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
439         activityIntent.putExtra("finish", true);
440         context.startActivity(activityIntent);
441     }
442 
doCreateFgslPendingIntent(Context context, Intent commandIntent)443     private PendingIntent doCreateFgslPendingIntent(Context context, Intent commandIntent) {
444         final String targetPackage = getTargetPackage(commandIntent);
445         final Intent intent = new Intent().setComponent(
446                 new ComponentName(targetPackage, FG_LOCATION_SERVICE_NAME));
447         int command = LocalForegroundServiceLocation.COMMAND_START_FOREGROUND_WITH_TYPE;
448         intent.putExtras(LocalForegroundService.newCommand(command));
449         final PendingIntent pendingIntent = PendingIntent.getForegroundService(context, 0,
450                 intent, PendingIntent.FLAG_IMMUTABLE);
451         sPendingIntent.put(targetPackage, pendingIntent);
452         return pendingIntent;
453     }
454 
doSendFgslPendingIntent(Context context, Intent commandIntent)455     private void doSendFgslPendingIntent(Context context, Intent commandIntent) {
456         final String targetPackage = getTargetPackage(commandIntent);
457         try {
458             ((PendingIntent) sPendingIntent.remove(targetPackage)).send();
459         } catch (PendingIntent.CanceledException e) {
460             Log.e(TAG, "Caugtht exception:", e);
461         }
462     }
463 
doStartChildProcess(Context context, Intent intent)464     private void doStartChildProcess(Context context, Intent intent) {
465         final Bundle extras = intent.getExtras();
466         final IBinder callback = extras.getBinder(EXTRA_CALLBACK);
467         final String[] cmdline = extras.getStringArray(EXTRA_CHILD_CMDLINE);
468         final Parcel data = Parcel.obtain();
469         final Parcel reply = Parcel.obtain();
470 
471         try {
472             sChildProcess = Runtime.getRuntime().exec(cmdline);
473             if (sChildProcess != null) {
474                 Log.i(TAG, "Forked child: " + sChildProcess);
475                 callback.transact(RESULT_CHILD_PROCESS_STARTED, data, reply, 0);
476             } // else the remote will fail with timeout
477         } catch (Exception e) {
478             Log.e(TAG, "Unable to execute command", e);
479             sChildProcess = null;
480         } finally {
481             data.recycle();
482             reply.recycle();
483         }
484     }
485 
doStopChildProcess(Context context, Intent intent)486     private void doStopChildProcess(Context context, Intent intent) {
487         final Bundle extras = intent.getExtras();
488         final IBinder callback = extras.getBinder(EXTRA_CALLBACK);
489         final long timeout = extras.getLong(EXTRA_TIMEOUT);
490         waitForChildProcessGone(true, callback, RESULT_CHILD_PROCESS_STOPPED, timeout);
491     }
492 
doWaitForChildProcessGone(Context context, Intent intent)493     private void doWaitForChildProcessGone(Context context, Intent intent) {
494         final Bundle extras = intent.getExtras();
495         final IBinder callback = extras.getBinder(EXTRA_CALLBACK);
496         final long timeout = extras.getLong(EXTRA_TIMEOUT);
497         waitForChildProcessGone(false, callback, RESULT_CHILD_PROCESS_GONE, timeout);
498     }
499 
waitForChildProcessGone(final boolean destroy, final IBinder callback, final int transactionCode, final long timeout)500     private static synchronized void waitForChildProcessGone(final boolean destroy,
501             final IBinder callback, final int transactionCode, final long timeout) {
502         if (destroy) {
503             sChildProcess.destroy();
504         }
505         new Thread(() -> {
506             final Parcel data = Parcel.obtain();
507             final Parcel reply = Parcel.obtain();
508             try {
509                 if (sChildProcess != null && sChildProcess.isAlive()) {
510                     final boolean exit = sChildProcess.waitFor(timeout, TimeUnit.MILLISECONDS);
511                     if (exit) {
512                         Log.i(TAG, "Child process died: " + sChildProcess);
513                         callback.transact(transactionCode, data, reply, 0);
514                     } else {
515                         Log.w(TAG, "Child process is still alive: " + sChildProcess);
516                     }
517                 } else {
518                     callback.transact(transactionCode, data, reply, 0);
519                 }
520             } catch (Exception e) {
521                 Log.e(TAG, "Error", e);
522             } finally {
523                 data.recycle();
524                 reply.recycle();
525             }
526         }).start();
527     }
528 
529     /**
530      * Directly call IActivityManager.startService() using a spoofed packageName which is known to
531      * be allowlisted by Android framework to be able to start foreground service
532      * from the background. Framework will disallow the foreground service to start from the
533      * background and a ForegroundServiceStartNotAllowedException will be caught.
534      * @param context
535      * @param commandIntent
536      */
doStartForegroundServiceSpoofPackageName(Context context, Intent commandIntent)537     private void doStartForegroundServiceSpoofPackageName(Context context, Intent commandIntent) {
538         String targetPackage = getTargetPackage(commandIntent);
539         Intent fgsIntent = new Intent();
540         fgsIntent.putExtras(commandIntent);
541         fgsIntent.setComponent(new ComponentName(targetPackage, FG_SERVICE_NAME));
542         int command = LocalForegroundService.COMMAND_START_FOREGROUND;
543         fgsIntent.putExtras(LocalForegroundService.newCommand(command));
544         try {
545             final PackageManager pm = context.getPackageManager();
546             String spoofPackageName = pm.getAttentionServicePackageName();
547             if (TextUtils.isEmpty(spoofPackageName)) {
548                 Log.d(TAG, "getAttentionServicePackageName() returns empty");
549                 spoofPackageName = pm.getSystemCaptionsServicePackageName();
550             }
551             if (TextUtils.isEmpty(spoofPackageName)) {
552                 Log.d(TAG, "getSystemCaptionsServicePackageName() returns empty");
553                 spoofPackageName = "android";
554             }
555             Log.d(TAG, "spoofPackageName: " + spoofPackageName);
556             final IBinder activityProxy = android.os.ServiceManager.getService("activity");
557             // Call IActivityManager.startService() directly using a spoofed packageName.
558             IActivityManager.Stub.asInterface(activityProxy).startService(
559                     context.getIApplicationThread(),
560                     fgsIntent,
561                     null,
562                     true,
563                     spoofPackageName,
564                     null,
565                     android.os.Process.myUserHandle().getIdentifier()
566             );
567         } catch (ForegroundServiceStartNotAllowedException e) {
568             Log.d(TAG, "startForegroundService gets an "
569                     + " ForegroundServiceStartNotAllowedException", e);
570         } catch (LinkageError e) {
571             // IActivityManager.startService() is a hidden API, access hidden API could get
572             // LinkageError, consider the test as pass if we get LinkageError.
573             Log.d(TAG, "startForegroundService gets an LinkageError", e);
574         } catch (RemoteException e) {
575             Log.d(TAG, "startForegroundService gets an RemoteException", e);
576         }
577     }
578 
doStartMediaPlayback(Context context, RemoteCallback callback)579     private void doStartMediaPlayback(Context context, RemoteCallback callback) {
580         mMediaSession = new MediaSession(context, TAG);
581         mMediaSession.setCallback(new MediaSession.Callback() {
582             @Override
583             public void onPlay() {
584                 super.onPlay();
585                 final Intent fgsIntent = new Intent(context, LocalForegroundService.class);
586                 fgsIntent.putExtras(LocalForegroundService.newCommand(
587                         LocalForegroundService.COMMAND_START_FOREGROUND));
588                 try {
589                     context.startForegroundService(fgsIntent);
590                 } catch (ForegroundServiceStartNotAllowedException e) {
591                     Log.e(TAG, "Error while trying to start an FGS", e);
592                 }
593             }
594 
595             @Override
596             public void onPause() {
597                 super.onPause();
598                 final Intent intent = new Intent(context, LocalForegroundService.class);
599                 intent.putExtras(LocalForegroundService.newCommand(
600                         LocalForegroundService.COMMAND_STOP_FOREGROUND_DONT_REMOVE_NOTIFICATION));
601                 context.startService(intent);
602             }
603 
604             @Override
605             public void onStop() {
606                 super.onStop();
607                 final Intent intent = new Intent(context, LocalForegroundService.class);
608                 context.stopService(intent);
609                 mMediaSession.release();
610             }
611         });
612         mMediaSession.setActive(true);
613 
614         callback.sendResult(null);
615     }
616 
617     /** Use FGS delegate to promote the app's procstate and provide keep-alive. */
doCreateMediaSession(Context context, RemoteCallback callback)618     private void doCreateMediaSession(Context context, RemoteCallback callback) {
619         mMediaSession = new MediaSession(context, TAG);
620         mMediaSession.setCallback(
621                 new MediaSession.Callback() {
622                     @Override
623                     public void onPlay() {
624                         super.onPlay();
625                         setPlaybackState(PlaybackState.STATE_PLAYING, mMediaSession);
626                     }
627 
628                     @Override
629                     public void onPause() {
630                         super.onPause();
631                         setPlaybackState(PlaybackState.STATE_PAUSED, mMediaSession);
632                     }
633 
634                     @Override
635                     public void onStop() {
636                         super.onStop();
637                         setPlaybackState(PlaybackState.STATE_STOPPED, mMediaSession);
638                     }
639                 });
640         callback.sendResult(null);
641     }
642 
doChangeMediaSessionActiveState(boolean isActive, RemoteCallback callback)643     private void doChangeMediaSessionActiveState(boolean isActive, RemoteCallback callback) {
644         if (mMediaSession != null) {
645             mMediaSession.setActive(isActive);
646         }
647         callback.sendResult(null);
648     }
649 
doReleaseMediaSession(RemoteCallback callback)650     private void doReleaseMediaSession(RemoteCallback callback) {
651         if (mMediaSession != null) {
652             mMediaSession.release();
653         }
654         callback.sendResult(null);
655     }
656 
doSendStickyBroadcast(Context context)657     private IntentFilter doSendStickyBroadcast(Context context) {
658         final String action = "android.app.stubs.action.TEST";
659         final Intent stickyIntent = new Intent(action);
660         final Uri uri = new Uri.Builder()
661                 .scheme(SCHEME_CONTENT)
662                 .authority(TestProvider.AUTHORITY)
663                 .build();
664         stickyIntent.setData(uri);
665         context.sendStickyBroadcast(stickyIntent);
666         final IntentFilter intentFilter = new IntentFilter(action);
667         try {
668             intentFilter.addDataType(TestProvider.TYPE);
669         } catch (IntentFilter.MalformedMimeTypeException e) {
670             Log.e(TAG, "Error setting the data type: " + TestProvider.TYPE);
671             return null;
672         }
673         return intentFilter;
674 
675     }
676 
doCreateMediaNotification(Context context, RemoteCallback callback)677     private void doCreateMediaNotification(Context context, RemoteCallback callback) {
678         NotificationManager notificationManager =
679                 context.getSystemService(NotificationManager.class);
680         maybeCreateNotificationChannel(notificationManager);
681         Notification notification =
682                 new Notification.Builder(context, mNotificationChannelId)
683                         .setContentTitle("Track title")
684                         .setSmallIcon(R.drawable.ic_call_answer)
685                         .setContentText("Artist - Album")
686                         .setStyle(
687                                 new Notification.MediaStyle()
688                                         .setMediaSession(mMediaSession.getSessionToken()))
689                         .setAutoCancel(false)
690                         .build();
691 
692         notificationManager.notify(mNotificationId++, notification);
693         callback.sendResult(null);
694     }
695 
maybeCreateNotificationChannel(NotificationManager notificationManager)696     private void maybeCreateNotificationChannel(NotificationManager notificationManager) {
697         if (mNotificationChannelId != null) {
698             return;
699         }
700         mNotificationChannelId = NOTIFICATION_CHANNEL_ID;
701         // Create the NotificationChannel, but only on API 26+ because
702         // the NotificationChannel class is new and not in the support library
703         CharSequence name = "Channel";
704         String description = "Description";
705         int importance = NotificationManager.IMPORTANCE_LOW;
706         NotificationChannel channel =
707                 new NotificationChannel(mNotificationChannelId, name.toString(), importance);
708         channel.setDescription(description);
709 
710         // Register the channel with the system; you can't change the importance
711         // or other notification behaviors after this
712         notificationManager.createNotificationChannel(channel);
713     }
714 
doAcquireProvider(Context context, Intent intent)715     private void doAcquireProvider(Context context, Intent intent) {
716         final Bundle extras = intent.getExtras();
717         final Uri uri = extras.getParcelable(EXTRA_URI, Uri.class);
718         final IContentProvider provider = context.getContentResolver().acquireProvider(uri);
719         sContentProviders.put(uri, provider);
720     }
721 
doReleaseProvider(Context context, Intent intent)722     private void doReleaseProvider(Context context, Intent intent) {
723         final Bundle extras = intent.getExtras();
724         final Uri uri = extras.getParcelable(EXTRA_URI, Uri.class);
725         final IContentProvider provider = sContentProviders.remove(uri);
726         if (provider == null) return;
727         context.getContentResolver().releaseProvider(provider);
728     }
729 
setPlaybackState(int state, MediaSession mediaSession)730     private void setPlaybackState(int state, MediaSession mediaSession) {
731         final long allActions = PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PAUSE
732                 | PlaybackState.ACTION_PLAY_PAUSE | PlaybackState.ACTION_STOP
733                 | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS
734                 | PlaybackState.ACTION_FAST_FORWARD | PlaybackState.ACTION_REWIND;
735         PlaybackState playbackState = new PlaybackState.Builder().setActions(allActions)
736                 .setState(state, 0L, 0.0f).build();
737         mediaSession.setPlaybackState(playbackState);
738     }
739 
doSetMediaSessionPlaybackState( @laybackState.State int state, RemoteCallback callback)740     private void doSetMediaSessionPlaybackState(
741             @PlaybackState.State int state, RemoteCallback callback) {
742         if (mMediaSession != null) {
743             setPlaybackState(state, mMediaSession);
744         }
745         callback.sendResult(null);
746     }
747 
getTargetPackage(Intent intent)748     private String getTargetPackage(Intent intent) {
749         return intent.getStringExtra(EXTRA_TARGET_PACKAGE);
750     }
751 
getFlags(Intent intent)752     private int getFlags(Intent intent) {
753         return intent.getIntExtra(EXTRA_FLAGS, 0);
754     }
755 
sendCommand(Context context, int command, String sourcePackage, String targetPackage, int flags, Bundle extras)756     public static void sendCommand(Context context, int command, String sourcePackage,
757             String targetPackage, int flags, Bundle extras) {
758         final Intent intent = makeIntent(command, sourcePackage, targetPackage, flags, extras);
759         Log.d(TAG, "Sending broadcast " + intent);
760         sendOrderedBroadcast(context, intent, null /* resultReceiver */,
761                 null /* broadcastOptions */);
762     }
763 
sendCommandWithResultReceiver(Context context, int command, String sourcePackage, String targetPackage, int flags, Bundle extras, BroadcastReceiver resultReceiver)764     public static void sendCommandWithResultReceiver(Context context, int command,
765             String sourcePackage, String targetPackage, int flags, Bundle extras,
766             BroadcastReceiver resultReceiver) {
767         final Intent intent = makeIntent(command, sourcePackage, targetPackage, flags, extras);
768         Log.d(TAG, "Sending broadcast with result receiver " + intent);
769         sendOrderedBroadcast(context, intent, resultReceiver, null /* broadcastOptions */);
770     }
771 
sendCommandWithBroadcastOptions(Context context, int command, String sourcePackage, String targetPackage, int flags, Bundle extras, BroadcastOptions broadcastOptions)772     public static void sendCommandWithBroadcastOptions(Context context, int command,
773             String sourcePackage, String targetPackage, int flags, Bundle extras,
774             BroadcastOptions broadcastOptions) {
775         final Intent intent = makeIntent(command, sourcePackage, targetPackage, flags, extras);
776         Log.d(TAG, "Sending broadcast with BroadcastOptions " + intent);
777         sendOrderedBroadcast(context, intent, null /* resultReceiver */, broadcastOptions);
778     }
779 
sendOrderedBroadcast(Context context, Intent intent, BroadcastReceiver resultReceiver, BroadcastOptions broadcastOptions)780     private static void sendOrderedBroadcast(Context context, Intent intent,
781             BroadcastReceiver resultReceiver, BroadcastOptions broadcastOptions) {
782         final BroadcastOptions effectiveOptions = broadcastOptions == null
783                 ? BroadcastOptions.makeBasic() : broadcastOptions;
784         effectiveOptions.setDebugLogEnabled(true);
785         context.sendOrderedBroadcast(intent, null /* receiverPermission */,
786                 effectiveOptions.toBundle(), resultReceiver, null /* scheduler */,
787                 Activity.RESULT_OK, null /* initialData */, null /* initialExtras */);
788     }
789 
makeIntent(int command, String sourcePackage, String targetPackage, int flags, Bundle extras)790     private static Intent makeIntent(int command, String sourcePackage,
791             String targetPackage, int flags, Bundle extras) {
792         Intent intent = new Intent();
793         if (command == COMMAND_BIND_SERVICE || command == COMMAND_START_FOREGROUND_SERVICE
794                 || command == COMMAND_STOP_FOREGROUND_SERVICE || command == COMMAND_START_ACTIVITY
795                 || command == COMMAND_START_FOREGROUND_SERVICE_LOCATION || command == COMMAND_UNBIND_SERVICE) {
796             intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
797         }
798         intent.setComponent(new ComponentName(sourcePackage, "android.app.stubs.CommandReceiver"));
799         intent.putExtra(EXTRA_COMMAND, command);
800         intent.putExtra(EXTRA_FLAGS, flags);
801         intent.putExtra(EXTRA_TARGET_PACKAGE, targetPackage);
802         if (extras != null) {
803             intent.putExtras(extras);
804         }
805         return intent;
806     }
807 
addServiceConnection(final String packageName)808     private ServiceConnection addServiceConnection(final String packageName) {
809         ServiceConnection connection = new ServiceConnection() {
810             @Override
811             public void onServiceConnected(ComponentName name, IBinder service) {
812             }
813 
814             @Override
815             public void onServiceDisconnected(ComponentName name) {
816             }
817         };
818         sServiceMap.put(packageName, connection);
819         return connection;
820     }
821 }
822