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 58 public void onReceive(Context context, Intent intent) { 59 int command = intent.getIntExtra(EXTRA_COMMAND, -1); 60 Log.d(TAG + "_" + context.getPackageName(), "Got command " + command + ", intent=" 61 + intent); 62 switch (command) { 63 case COMMAND_BIND_SERVICE: 64 doBindService(context, intent); 65 break; 66 case COMMAND_UNBIND_SERVICE: 67 doUnbindService(context, intent); 68 break; 69 case COMMAND_START_FOREGROUND_SERVICE: 70 doStartForegroundService(context, LocalForegroundService.class); 71 break; 72 case COMMAND_STOP_FOREGROUND_SERVICE: 73 doStopForegroundService(context, LocalForegroundService.class); 74 break; 75 case COMMAND_START_FOREGROUND_SERVICE_LOCATION: 76 int type = intent.getIntExtra( 77 LocalForegroundServiceLocation.EXTRA_FOREGROUND_SERVICE_TYPE, 78 ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST); 79 doStartForegroundServiceWithType(context, LocalForegroundServiceLocation.class, 80 type); 81 break; 82 case COMMAND_STOP_FOREGROUND_SERVICE_LOCATION: 83 doStopForegroundService(context, LocalForegroundServiceLocation.class); 84 break; 85 } 86 } 87 88 private void doBindService(Context context, Intent commandIntent) { 89 context = context.getApplicationContext(); 90 if (LocalService.sServiceContext != null) { 91 context = LocalService.sServiceContext; 92 } 93 94 String targetPackage = getTargetPackage(commandIntent); 95 int flags = getFlags(commandIntent); 96 97 Intent bindIntent = new Intent(); 98 bindIntent.setComponent(new ComponentName(targetPackage, SERVICE_NAME)); 99 100 ServiceConnection connection = addServiceConnection(targetPackage); 101 102 context.bindService(bindIntent, connection, flags | Context.BIND_AUTO_CREATE); 103 } 104 105 private void doUnbindService(Context context, Intent commandIntent) { 106 String targetPackage = getTargetPackage(commandIntent); 107 context.unbindService(sServiceMap.remove(targetPackage)); 108 } 109 110 private void doStartForegroundService(Context context, Class cls) { 111 Intent fgsIntent = new Intent(context, cls); 112 int command = LocalForegroundService.COMMAND_START_FOREGROUND; 113 fgsIntent.putExtras(LocalForegroundService.newCommand(new Binder(), command)); 114 context.startForegroundService(fgsIntent); 115 } 116 117 private void doStartForegroundServiceWithType(Context context, Class cls, int type) { 118 Intent fgsIntent = new Intent(context, cls); 119 int command = LocalForegroundServiceLocation.COMMAND_START_FOREGROUND_WITH_TYPE; 120 fgsIntent.putExtras(LocalForegroundService.newCommand(new Binder(), command)); 121 fgsIntent.putExtra(LocalForegroundServiceLocation.EXTRA_FOREGROUND_SERVICE_TYPE, type); 122 context.startForegroundService(fgsIntent); 123 } 124 125 private void doStopForegroundService(Context context, Class cls) { 126 Intent fgsIntent = new Intent(context, cls); 127 context.stopService(fgsIntent); 128 } 129 130 private String getTargetPackage(Intent intent) { 131 return intent.getStringExtra(EXTRA_TARGET_PACKAGE); 132 } 133 134 private int getFlags(Intent intent) { 135 return intent.getIntExtra(EXTRA_FLAGS, 0); 136 } 137 138 public static void sendCommand(Context context, int command, String sourcePackage, 139 String targetPackage, int flags, Bundle extras) { 140 Intent intent = new Intent(); 141 if (command == COMMAND_BIND_SERVICE || command == COMMAND_START_FOREGROUND_SERVICE) { 142 intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); 143 } 144 intent.setComponent(new ComponentName(sourcePackage, "android.app.stubs.CommandReceiver")); 145 intent.putExtra(EXTRA_COMMAND, command); 146 intent.putExtra(EXTRA_FLAGS, flags); 147 intent.putExtra(EXTRA_TARGET_PACKAGE, targetPackage); 148 if (extras != null) { 149 intent.putExtras(extras); 150 } 151 sendCommand(context, intent); 152 } 153 154 private static void sendCommand(Context context, Intent intent) { 155 Log.d(TAG, "Sending broadcast " + intent); 156 context.sendOrderedBroadcast(intent, null); 157 } 158 159 private ServiceConnection addServiceConnection(final String packageName) { 160 ServiceConnection connection = new ServiceConnection() { 161 @Override 162 public void onServiceConnected(ComponentName name, IBinder service) { 163 } 164 165 @Override 166 public void onServiceDisconnected(ComponentName name) { 167 } 168 }; 169 sServiceMap.put(packageName, connection); 170 return connection; 171 } 172 } 173