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