blob: 5a13eabeebe0c7b51fe8a11a0a08f26dda3cac83 [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.app.ActivityManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
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 int COMMAND_START_ALERT_SERVICE = 7;
public static final int COMMAND_STOP_ALERT_SERVICE = 8;
public static final int COMMAND_SELF_INDUCED_ANR = 9;
public static final int COMMAND_START_ACTIVITY = 10;
public static final int COMMAND_STOP_ACTIVITY = 11;
public static final int COMMAND_CREATE_FGSL_PENDING_INTENT = 12;
public static final int COMMAND_SEND_FGSL_PENDING_INTENT = 13;
public static final int COMMAND_BIND_FOREGROUND_SERVICE = 14;
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";
public static final String FG_SERVICE_NAME = "android.app.stubs.LocalForegroundService";
public static final String FG_LOCATION_SERVICE_NAME =
"android.app.stubs.LocalForegroundServiceLocation";
public static final String ACTIVITY_NAME = "android.app.stubs.SimpleActivity";
private static ArrayMap<String,ServiceConnection> sServiceMap = new ArrayMap<>();
// Map a packageName to a Intent that starts an Activity.
private static ArrayMap<String, Intent> sActivityIntent = new ArrayMap<>();
// Map a packageName to a PendingIntent.
private static ArrayMap<String, PendingIntent> sPendingIntent = 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) {
// Use the application context as the receiver context could be restricted.
context = context.getApplicationContext();
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, SERVICE_NAME);
break;
case COMMAND_UNBIND_SERVICE:
doUnbindService(context, intent);
break;
case COMMAND_START_FOREGROUND_SERVICE:
doStartForegroundService(context, intent);
break;
case COMMAND_STOP_FOREGROUND_SERVICE:
doStopForegroundService(context, intent, FG_SERVICE_NAME);
break;
case COMMAND_START_FOREGROUND_SERVICE_LOCATION:
doStartForegroundServiceWithType(context, intent);
break;
case COMMAND_STOP_FOREGROUND_SERVICE_LOCATION:
doStopForegroundService(context, intent, FG_LOCATION_SERVICE_NAME);
break;
case COMMAND_START_ALERT_SERVICE:
doStartAlertService(context);
break;
case COMMAND_STOP_ALERT_SERVICE:
doStopAlertService(context);
break;
case COMMAND_SELF_INDUCED_ANR:
doSelfInducedAnr(context);
break;
case COMMAND_START_ACTIVITY:
doStartActivity(context, intent);
break;
case COMMAND_STOP_ACTIVITY:
doStopActivity(context, intent);
break;
case COMMAND_CREATE_FGSL_PENDING_INTENT:
doCreateFgslPendingIntent(context, intent);
break;
case COMMAND_SEND_FGSL_PENDING_INTENT:
doSendFgslPendingIntent(context, intent);
break;
case COMMAND_BIND_FOREGROUND_SERVICE:
doBindService(context, intent, FG_LOCATION_SERVICE_NAME);
break;
}
}
private void doBindService(Context context, Intent commandIntent, String serviceName) {
String targetPackage = getTargetPackage(commandIntent);
int flags = getFlags(commandIntent);
Intent bindIntent = new Intent();
bindIntent.setComponent(new ComponentName(targetPackage, serviceName));
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, Intent commandIntent) {
String targetPackage = getTargetPackage(commandIntent);
Intent fgsIntent = new Intent();
fgsIntent.setComponent(new ComponentName(targetPackage, FG_SERVICE_NAME));
int command = LocalForegroundService.COMMAND_START_FOREGROUND;
fgsIntent.putExtras(LocalForegroundService.newCommand(new Binder(), command));
context.startForegroundService(fgsIntent);
}
private void doStartForegroundServiceWithType(Context context, Intent commandIntent) {
String targetPackage = getTargetPackage(commandIntent);
Intent fgsIntent = new Intent();
fgsIntent.putExtras(commandIntent); // include the fg service type if any.
fgsIntent.setComponent(new ComponentName(targetPackage, FG_LOCATION_SERVICE_NAME));
int command = LocalForegroundServiceLocation.COMMAND_START_FOREGROUND_WITH_TYPE;
fgsIntent.putExtras(LocalForegroundService.newCommand(new Binder(), command));
context.startForegroundService(fgsIntent);
}
private void doStopForegroundService(Context context, Intent commandIntent,
String serviceName) {
String targetPackage = getTargetPackage(commandIntent);
Intent fgsIntent = new Intent();
fgsIntent.setComponent(new ComponentName(targetPackage, serviceName));
context.stopService(fgsIntent);
}
private void doStartAlertService(Context context) {
Intent intent = new Intent(context, LocalAlertService.class);
intent.setAction(LocalAlertService.COMMAND_SHOW_ALERT);
context.startService(intent);
}
private void doStopAlertService(Context context) {
Intent intent = new Intent(context, LocalAlertService.class);
intent.setAction(LocalAlertService.COMMAND_HIDE_ALERT);
context.startService(intent);
}
private void doSelfInducedAnr(Context context) {
ActivityManager am = context.getSystemService(ActivityManager.class);
am.appNotResponding("CTS - self induced");
}
private void doStartActivity(Context context, Intent commandIntent) {
String targetPackage = getTargetPackage(commandIntent);
Intent activityIntent = new Intent(Intent.ACTION_MAIN);
sActivityIntent.put(targetPackage, activityIntent);
activityIntent.setComponent(new ComponentName(targetPackage, ACTIVITY_NAME));
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(activityIntent);
}
private void doStopActivity(Context context, Intent commandIntent) {
String targetPackage = getTargetPackage(commandIntent);
Intent activityIntent = sActivityIntent.remove(targetPackage);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
activityIntent.putExtra("finish", true);
context.startActivity(activityIntent);
}
private void doCreateFgslPendingIntent(Context context, Intent commandIntent) {
final String targetPackage = getTargetPackage(commandIntent);
final Intent intent = new Intent().setComponent(
new ComponentName(targetPackage, FG_LOCATION_SERVICE_NAME));
int command = LocalForegroundServiceLocation.COMMAND_START_FOREGROUND_WITH_TYPE;
intent.putExtras(LocalForegroundService.newCommand(new Binder(), command));
final PendingIntent pendingIntent = PendingIntent.getForegroundService(context, 0,
intent, 0);
sPendingIntent.put(targetPackage, pendingIntent);
}
private void doSendFgslPendingIntent(Context context, Intent commandIntent) {
final String targetPackage = getTargetPackage(commandIntent);
try {
((PendingIntent) sPendingIntent.remove(targetPackage)).send();
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Caugtht exception:", e);
}
}
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;
}
}