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.content.BroadcastReceiver;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.ServiceConnection;
24 import android.content.pm.ServiceInfo;
25 import android.os.Binder;
26 import android.os.Bundle;
27 import android.os.IBinder;
28 import android.util.ArrayMap;
29 import android.util.Log;
30 
31 public class CommandReceiver extends BroadcastReceiver {
32 
33     private static final String TAG = "CommandReceiver";
34 
35     // Requires flags and targetPackage
36     public static final int COMMAND_BIND_SERVICE = 1;
37     // Requires targetPackage
38     public static final int COMMAND_UNBIND_SERVICE = 2;
39     public static final int COMMAND_START_FOREGROUND_SERVICE = 3;
40     public static final int COMMAND_STOP_FOREGROUND_SERVICE = 4;
41     public static final int COMMAND_START_FOREGROUND_SERVICE_LOCATION = 5;
42     public static final int COMMAND_STOP_FOREGROUND_SERVICE_LOCATION = 6;
43 
44     public static final String EXTRA_COMMAND = "android.app.stubs.extra.COMMAND";
45     public static final String EXTRA_TARGET_PACKAGE = "android.app.stubs.extra.TARGET_PACKAGE";
46     public static final String EXTRA_FLAGS = "android.app.stubs.extra.FLAGS";
47 
48     public static final String SERVICE_NAME = "android.app.stubs.LocalService";
49 
50     private static ArrayMap<String,ServiceConnection> sServiceMap = new ArrayMap<>();
51 
52     /**
53      * Handle the different types of binding/unbinding requests.
54      * @param context The Context in which the receiver is running.
55      * @param intent The Intent being received.
56      */
57     @Override
onReceive(Context context, Intent intent)58     public void onReceive(Context context, Intent intent) {
59         // Use the application context as the receiver context could be restricted.
60         context = context.getApplicationContext();
61         int command = intent.getIntExtra(EXTRA_COMMAND, -1);
62         Log.d(TAG + "_" + context.getPackageName(), "Got command " + command + ", intent="
63                 + intent);
64         switch (command) {
65             case COMMAND_BIND_SERVICE:
66                 doBindService(context, intent);
67                 break;
68             case COMMAND_UNBIND_SERVICE:
69                 doUnbindService(context, intent);
70                 break;
71             case COMMAND_START_FOREGROUND_SERVICE:
72                 doStartForegroundService(context, LocalForegroundService.class);
73                 break;
74             case COMMAND_STOP_FOREGROUND_SERVICE:
75                 doStopForegroundService(context, LocalForegroundService.class);
76                 break;
77             case COMMAND_START_FOREGROUND_SERVICE_LOCATION:
78                 int type = intent.getIntExtra(
79                         LocalForegroundServiceLocation.EXTRA_FOREGROUND_SERVICE_TYPE,
80                         ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST);
81                 doStartForegroundServiceWithType(context, LocalForegroundServiceLocation.class,
82                         type);
83                 break;
84             case COMMAND_STOP_FOREGROUND_SERVICE_LOCATION:
85                 doStopForegroundService(context, LocalForegroundServiceLocation.class);
86                 break;
87         }
88     }
89 
doBindService(Context context, Intent commandIntent)90     private void doBindService(Context context, Intent commandIntent) {
91         String targetPackage = getTargetPackage(commandIntent);
92         int flags = getFlags(commandIntent);
93 
94         Intent bindIntent = new Intent();
95         bindIntent.setComponent(new ComponentName(targetPackage, SERVICE_NAME));
96 
97         ServiceConnection connection = addServiceConnection(targetPackage);
98         context.bindService(bindIntent, connection, flags | Context.BIND_AUTO_CREATE);
99     }
100 
doUnbindService(Context context, Intent commandIntent)101     private void doUnbindService(Context context, Intent commandIntent) {
102         String targetPackage = getTargetPackage(commandIntent);
103         ServiceConnection connection = sServiceMap.remove(targetPackage);
104         context.unbindService(connection);
105     }
106 
doStartForegroundService(Context context, Class cls)107     private void doStartForegroundService(Context context, Class cls) {
108         Intent fgsIntent = new Intent(context, cls);
109         int command = LocalForegroundService.COMMAND_START_FOREGROUND;
110         fgsIntent.putExtras(LocalForegroundService.newCommand(new Binder(), command));
111         context.startForegroundService(fgsIntent);
112     }
113 
doStartForegroundServiceWithType(Context context, Class cls, int type)114     private void doStartForegroundServiceWithType(Context context, Class cls, int type) {
115         Intent fgsIntent = new Intent(context, cls);
116         int command = LocalForegroundServiceLocation.COMMAND_START_FOREGROUND_WITH_TYPE;
117         fgsIntent.putExtras(LocalForegroundService.newCommand(new Binder(), command));
118         fgsIntent.putExtra(LocalForegroundServiceLocation.EXTRA_FOREGROUND_SERVICE_TYPE, type);
119         context.startForegroundService(fgsIntent);
120     }
121 
doStopForegroundService(Context context, Class cls)122     private void doStopForegroundService(Context context, Class cls) {
123         Intent fgsIntent = new Intent(context, cls);
124         context.stopService(fgsIntent);
125     }
126 
getTargetPackage(Intent intent)127     private String getTargetPackage(Intent intent) {
128         return intent.getStringExtra(EXTRA_TARGET_PACKAGE);
129     }
130 
getFlags(Intent intent)131     private int getFlags(Intent intent) {
132         return intent.getIntExtra(EXTRA_FLAGS, 0);
133     }
134 
sendCommand(Context context, int command, String sourcePackage, String targetPackage, int flags, Bundle extras)135     public static void sendCommand(Context context, int command, String sourcePackage,
136             String targetPackage, int flags, Bundle extras) {
137         Intent intent = new Intent();
138         if (command == COMMAND_BIND_SERVICE || command == COMMAND_START_FOREGROUND_SERVICE) {
139             intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
140         }
141         intent.setComponent(new ComponentName(sourcePackage, "android.app.stubs.CommandReceiver"));
142         intent.putExtra(EXTRA_COMMAND, command);
143         intent.putExtra(EXTRA_FLAGS, flags);
144         intent.putExtra(EXTRA_TARGET_PACKAGE, targetPackage);
145         if (extras != null) {
146             intent.putExtras(extras);
147         }
148         sendCommand(context, intent);
149     }
150 
sendCommand(Context context, Intent intent)151     private static void sendCommand(Context context, Intent intent) {
152         Log.d(TAG, "Sending broadcast " + intent);
153         context.sendOrderedBroadcast(intent, null);
154     }
155 
addServiceConnection(final String packageName)156     private ServiceConnection addServiceConnection(final String packageName) {
157         ServiceConnection connection = new ServiceConnection() {
158             @Override
159             public void onServiceConnected(ComponentName name, IBinder service) {
160             }
161 
162             @Override
163             public void onServiceDisconnected(ComponentName name) {
164             }
165         };
166         sServiceMap.put(packageName, connection);
167         return connection;
168     }
169 }
170