blob: a7d2c3f28b414e2a5031c63955c94374939b5307 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.stubs;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.util.ArrayMap;
import android.util.Log;
public class CommandReceiver extends BroadcastReceiver {
private static final String TAG = "CommandReceiver";
// Requires flags and targetPackage
public static final int COMMAND_BIND_SERVICE = 1;
// Requires targetPackage
public static final int COMMAND_UNBIND_SERVICE = 2;
public static final int COMMAND_START_FOREGROUND_SERVICE = 3;
public static final int COMMAND_STOP_FOREGROUND_SERVICE = 4;
public static final int COMMAND_START_FOREGROUND_SERVICE_LOCATION = 5;
public static final int COMMAND_STOP_FOREGROUND_SERVICE_LOCATION = 6;
public static final String EXTRA_COMMAND = "android.app.stubs.extra.COMMAND";
public static final String EXTRA_TARGET_PACKAGE = "android.app.stubs.extra.TARGET_PACKAGE";
public static final String EXTRA_FLAGS = "android.app.stubs.extra.FLAGS";
public static final String SERVICE_NAME = "android.app.stubs.LocalService";
private static ArrayMap<String,ServiceConnection> sServiceMap = new ArrayMap<>();
/**
* Handle the different types of binding/unbinding requests.
* @param context The Context in which the receiver is running.
* @param intent The Intent being received.
*/
@Override
public void onReceive(Context context, Intent intent) {
int command = intent.getIntExtra(EXTRA_COMMAND, -1);
Log.d(TAG + "_" + context.getPackageName(), "Got command " + command + ", intent="
+ intent);
switch (command) {
case COMMAND_BIND_SERVICE:
doBindService(context, intent);
break;
case COMMAND_UNBIND_SERVICE:
doUnbindService(context, intent);
break;
case COMMAND_START_FOREGROUND_SERVICE:
doStartForegroundService(context, LocalForegroundService.class);
break;
case COMMAND_STOP_FOREGROUND_SERVICE:
doStopForegroundService(context, LocalForegroundService.class);
break;
case COMMAND_START_FOREGROUND_SERVICE_LOCATION:
int type = intent.getIntExtra(
LocalForegroundServiceLocation.EXTRA_FOREGROUND_SERVICE_TYPE,
ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST);
doStartForegroundServiceWithType(context, LocalForegroundServiceLocation.class,
type);
break;
case COMMAND_STOP_FOREGROUND_SERVICE_LOCATION:
doStopForegroundService(context, LocalForegroundServiceLocation.class);
break;
}
}
private void doBindService(Context context, Intent commandIntent) {
context = context.getApplicationContext();
if (LocalService.sServiceContext != null) {
context = LocalService.sServiceContext;
}
String targetPackage = getTargetPackage(commandIntent);
int flags = getFlags(commandIntent);
Intent bindIntent = new Intent();
bindIntent.setComponent(new ComponentName(targetPackage, SERVICE_NAME));
ServiceConnection connection = addServiceConnection(targetPackage);
context.bindService(bindIntent, connection, flags | Context.BIND_AUTO_CREATE);
}
private void doUnbindService(Context context, Intent commandIntent) {
String targetPackage = getTargetPackage(commandIntent);
context.unbindService(sServiceMap.remove(targetPackage));
}
private void doStartForegroundService(Context context, Class cls) {
Intent fgsIntent = new Intent(context, cls);
int command = LocalForegroundService.COMMAND_START_FOREGROUND;
fgsIntent.putExtras(LocalForegroundService.newCommand(new Binder(), command));
context.startForegroundService(fgsIntent);
}
private void doStartForegroundServiceWithType(Context context, Class cls, int type) {
Intent fgsIntent = new Intent(context, cls);
int command = LocalForegroundServiceLocation.COMMAND_START_FOREGROUND_WITH_TYPE;
fgsIntent.putExtras(LocalForegroundService.newCommand(new Binder(), command));
fgsIntent.putExtra(LocalForegroundServiceLocation.EXTRA_FOREGROUND_SERVICE_TYPE, type);
context.startForegroundService(fgsIntent);
}
private void doStopForegroundService(Context context, Class cls) {
Intent fgsIntent = new Intent(context, cls);
context.stopService(fgsIntent);
}
private String getTargetPackage(Intent intent) {
return intent.getStringExtra(EXTRA_TARGET_PACKAGE);
}
private int getFlags(Intent intent) {
return intent.getIntExtra(EXTRA_FLAGS, 0);
}
public static void sendCommand(Context context, int command, String sourcePackage,
String targetPackage, int flags, Bundle extras) {
Intent intent = new Intent();
if (command == COMMAND_BIND_SERVICE || command == COMMAND_START_FOREGROUND_SERVICE) {
intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
}
intent.setComponent(new ComponentName(sourcePackage, "android.app.stubs.CommandReceiver"));
intent.putExtra(EXTRA_COMMAND, command);
intent.putExtra(EXTRA_FLAGS, flags);
intent.putExtra(EXTRA_TARGET_PACKAGE, targetPackage);
if (extras != null) {
intent.putExtras(extras);
}
sendCommand(context, intent);
}
private static void sendCommand(Context context, Intent intent) {
Log.d(TAG, "Sending broadcast " + intent);
context.sendOrderedBroadcast(intent, null);
}
private ServiceConnection addServiceConnection(final String packageName) {
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
sServiceMap.put(packageName, connection);
return connection;
}
}