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